From f1e76979b6639b76bc71094270ca279f551c660c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 11 Sep 2001 19:00:18 +0000 Subject: [PATCH] Date: Sat, 8 Sep 2001 04:42:23 +0200 From: Max Horn Subject: SDL/OSX: Joystick; Better key handling I just finished implementing improved keyhandling for OS X (in fact the code should be easily ported to the "normal" MacOS part of SDL, I just had no chance yet). Works like this: First init the mapping table statically like before. Them, it queries the OS for the "official" key table, then iterates over all 127 scancode and gets the associates ascii code. It ignores everythng below 32 (has to, as it would lead to many problems if we did not... e.g. both ESC and NUM LOCk produce an ascii code 27 on my keyboard), and all stuff above 127 is mapped to SDLK_WORLD_* simply in the order it is encountered. In addition, caps lock is now working, too. The code work flawless for me, but since I only have one keyboard, I may have not encountered some serious problem... but I am pretty confident that it is better than the old code in most cases. The joystick driver works fine for me, too. I think it can be added to CVS already. It would simply be helpful if more people would test it. Hm, I wonder if Maelstrom or GLTron has Joystick support? That would be a wonderful test application :) I also took the liberty of modifying some text files like BUGS, README.CVS, README.MacOSX (which now contains the OS X docs I long promised) --- BUGS | 6 +- README.CVS | 8 +- README.MacOS | 7 +- README.MacOSX | 80 ++- configure.in | 6 +- sdl.m4 | 1 + src/joystick/Makefile.am | 2 +- src/joystick/darwin/.cvsignore | 6 + src/joystick/darwin/Makefile.am | 8 + src/joystick/darwin/SDL_sysjoystick.c | 776 ++++++++++++++++++++++++++ src/video/quartz/SDL_QuartzEvents.m | 561 +++++++++++-------- src/video/quartz/SDL_QuartzVideo.m | 2 - test/acinclude.m4 | 1 + 13 files changed, 1197 insertions(+), 267 deletions(-) create mode 100644 src/joystick/darwin/.cvsignore create mode 100644 src/joystick/darwin/Makefile.am create mode 100644 src/joystick/darwin/SDL_sysjoystick.c diff --git a/BUGS b/BUGS index 509636665..5d20bc5d8 100644 --- a/BUGS +++ b/BUGS @@ -68,9 +68,9 @@ MacOS: Not all of the keys are properly recognized on the keyboard. MacOS X: - Joystick and CD-ROM functions are not implemented yet. + CD-ROM functions are not implemented yet. - Window management buttons don't draw correctly. + Joystick code is not extensively tested yet. Window may not close when unsetting video mode and resetting. @@ -98,8 +98,6 @@ MacOS X: cursor in the center of the window/screen. Also, mouse moved events are not generated, and the keyboard cannot be grabbed. - Not all of the keys are properly recognized on the keyboard. - MacOS X seems to have a broken pthread_cancel() implementation. FreeBSD: diff --git a/README.CVS b/README.CVS index b223d525f..7e812edd0 100644 --- a/README.CVS +++ b/README.CVS @@ -1,13 +1,13 @@ The latest development version of SDL is available via CVS: -cvs -d :pserver:guest@cvs.lokigames.com:/cvs login -# use the password "guest" -cvs -d :pserver:guest@cvs.lokigames.com:/cvs checkout SDL +cvs -d :pserver:guest@libsdl.org:/home/slouken/libsdl.org/cvs login +# No password, so just hit enter when prompted for a password +cvs -d :pserver:guest@libsdl.org:/home/slouken/libsdl.org/cvs checkout SDL When you check a fresh copy of SDL out of CVS, you need to generate the files used by make by running the "autogen.sh" script, which will run aclocal, automake, autoconf and then run configure. -There is a web interface to cvs at http://cvs.lokigames.com/ +There is a web interface to cvs at http://www.libsdl.org/cgi/cvsweb.cgi diff --git a/README.MacOS b/README.MacOS index 0b1de3e64..acfd93578 100644 --- a/README.MacOS +++ b/README.MacOS @@ -52,9 +52,12 @@ IV. Enjoy! :) If you have a project you'd like me to know about, or want to ask questions, go ahead and join the SDL developer's mailing list by sending e-mail to: - majordomo@lokigames.com + sdl-request@libsdl.org - and put the line "subscribe sdl" in the body of the message. + and put "subscribe" into the subject of the message. Or alternatively you + can use the web interface: + + http://www.libsdl.org/mailman/listinfo/sdl ============================================================================== diff --git a/README.MacOSX b/README.MacOSX index 2c48c1323..235cc080c 100644 --- a/README.MacOSX +++ b/README.MacOSX @@ -18,11 +18,7 @@ process: (You may need to create the subdirs of /usr/local manually.) -For some reason, libtool doesn't run ranlib properly, so do this -manually: - - ranlib /usr/local/lib/libSDL.a - +/* To use the library once it's built, you need to use the "Carbon framework", which is the port of the old Mac Toolbox to OS X. To do this, use the -F and -framework arguments for compiling @@ -33,6 +29,79 @@ and linking, respectively: sdl-config knows about the linking path and -framework, so it's recommended to use it to fill in your Makefile variables. +*/ + +To use the library once it's built, you essential have two possibilities: +use the traditional autoconf/automake/make method, or use Apple's Project Builder. + +============================================================================== +Using the Simple DirectMedia Layer with a traditional Makefile +============================================================================== + +In the following, it will be mostly assumed that you are using autoconf and +automake to setup your SDL project, and furthermore that you use the AM_PATH_SDL +macro provided by SDL in sdl.m4. If you are not using these tools, you can +still use SDL but it will be somewhat hard to get running. + +Only step 1) is really required to get started, but for full OS X support you +will want to do the other steps, too. + +1) Update your acinclude.m4 file in case you have copied an older version of + sdl.m4 into it. This is essential as AM_PATH_SDL now performs some additional + tasks when used on MacOS X + + Rationale: AM_PATH_SDL copies /usr/local/share/sdl/Info.plist and the folder + /usr/local/share/sdl/SDLMain.nib/ into the directory where configure is invoked. + This is essential for the configure script to be able to run the test code + that detects SDL. + +2) Copy SDL's Info.plist.in file (from src/main/macosx) into your project's main + folder (the same spot that your configure.in sits), and edit it to suite your + needs. Then add it to your AC_OUTPUT list in configure.in + + Rationale: The Info.plist file can be used to specify an icon file for + your app, and also to provide a human readable version/copyright string + and other meta-information to the user via the Finder's Get Info dialog. + +3) Add something like the following rule to your Makefile.am: + +APP_NAME.app: EXE_NAME + mkdir -p $@/Contents/MacOS + mkdir -p $@/Contents/Resources + mkdir -p $@/Contents/Resources/SDLMain.nib + echo "APPL????" > $@/Contents/PkgInfo + $(INSTALL_DATA) Info.plist $@/Contents/ + $(INSTALL_DATA) SDLMain.nib/*.nib $@/Contents/Resources/ + $(INSTALL_PROGRAM) $< $@/Contents/MacOS/ + + You should replace EXE_NAME with the name of the executable. APP_NAME is what + will be visible to the user in the Finder. Usually it will be the same + as EXE_NAME but capitalized. E.g. if EXE_NAME is "testgame" then APP_NAME + usually is "TestGame" + + If your project builds more than one application, you will have to do a bit more. + For each of your target applications, you need a seperate rule. Furthermore, each + needs its own Info.plist file, since that has to contain the exact name of the + executable (i.e. EXE_NAME above). One way to do that is to use sed in your make rules + and modify a single master Info.plist. + + Rationale: on Mac OS X, executables have to be put into so-called "bundles". + The make rule given above will construct such a bundle around the executable + for you. You need to make a copy of it for each target application. + +4) If you want the create bundles to be installed, you may want to add this + rule to your Makefile.am: + +install-exec-local: Exult.app + mkdir -p /Applications/ + cp -r $< /Applications/ + + This rule takes the Bundle created by the rule from step 3 and installs them + into /Applications/. An alternate installation place would be $HOME/Applications/ + + Again, if you want to install multiple applications, you will have to augment + the make rule accordingly. + ============================================================================== Using the Simple DirectMedia Layer with Project Builder @@ -122,3 +191,4 @@ following locations: but I expect that things will still work on older versions. Known bugs are listed in the file "BUGS" + LocalWords: Stuffit diff --git a/configure.in b/configure.in index 6b83669f9..aa957275f 100644 --- a/configure.in +++ b/configure.in @@ -2056,8 +2056,8 @@ case "$target" in fi # Set up files for the joystick library if test x$enable_joystick = xyes; then - JOYSTICK_SUBDIRS="$JOYSTICK_SUBDIRS dummy" - JOYSTICK_DRIVERS="$JOYSTICK_DRIVERS dummy/libjoystick_dummy.la" + JOYSTICK_SUBDIRS="$JOYSTICK_SUBDIRS darwin" + JOYSTICK_DRIVERS="$JOYSTICK_DRIVERS darwin/libjoystick_darwin.la" fi # Set up files for the cdrom library if test x$enable_cdrom = xyes; then @@ -2192,6 +2192,7 @@ include/Makefile src/Makefile src/main/Makefile src/main/macosx/Makefile +src/main/macosx/Info.plist src/audio/Makefile src/audio/alsa/Makefile src/audio/arts/Makefile @@ -2236,6 +2237,7 @@ src/events/Makefile src/joystick/Makefile src/joystick/amigaos/Makefile src/joystick/beos/Makefile +src/joystick/darwin/Makefile src/joystick/dummy/Makefile src/joystick/linux/Makefile src/joystick/macos/Makefile diff --git a/sdl.m4 b/sdl.m4 index d2a478594..294942364 100644 --- a/sdl.m4 +++ b/sdl.m4 @@ -32,6 +32,7 @@ AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run fi fi + AC_REQUIRE([AC_CANONICAL_TARGET]) AC_PATH_PROG(SDL_CONFIG, sdl-config, no) min_sdl_version=ifelse([$1], ,0.11.0,$1) AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) diff --git a/src/joystick/Makefile.am b/src/joystick/Makefile.am index efd9f9d8d..2e61f6373 100644 --- a/src/joystick/Makefile.am +++ b/src/joystick/Makefile.am @@ -5,7 +5,7 @@ noinst_LTLIBRARIES = libjoystick.la # Define which subdirectories need to be built SUBDIRS = @JOYSTICK_SUBDIRS@ -DIST_SUBDIRS = dummy amigaos beos linux macos win32 +DIST_SUBDIRS = dummy amigaos beos darwin linux macos win32 DRIVERS = @JOYSTICK_DRIVERS@ diff --git a/src/joystick/darwin/.cvsignore b/src/joystick/darwin/.cvsignore new file mode 100644 index 000000000..899d53557 --- /dev/null +++ b/src/joystick/darwin/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +.libs +*.o +*.lo +*.la diff --git a/src/joystick/darwin/Makefile.am b/src/joystick/darwin/Makefile.am new file mode 100644 index 000000000..06093550f --- /dev/null +++ b/src/joystick/darwin/Makefile.am @@ -0,0 +1,8 @@ + +## Makefile.am for the darwin/MacOS X joystick driver for SDL + +noinst_LTLIBRARIES = libjoystick_darwin.la +libjoystick_darwin_la_SOURCES = $(SRCS) + +# The SDL joystick driver sources +SRCS = SDL_sysjoystick.c diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c new file mode 100644 index 000000000..29f61fd58 --- /dev/null +++ b/src/joystick/darwin/SDL_sysjoystick.c @@ -0,0 +1,776 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +/* SDL joystick driver for Darwn / MacOS X, based on the IOKit HID API */ +/* Written 2001 by Max Horn */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SDL_error.h" +#include "SDL_joystick.h" +#include "SDL_sysjoystick.h" +#include "SDL_joystick_c.h" + +struct recElement +{ + IOHIDElementCookie cookie; // unique value which identifies element, will NOT change + long min; // reported min value possible + long max; // reported max value possible +/* + TODO: maybe should handle the following stuff somehow? + + long scaledMin; // reported scaled min value possible + long scaledMax; // reported scaled max value possible + long size; // size in bits of data return from element + Boolean relative; // are reports relative to last report (deltas) + Boolean wrapping; // does element wrap around (one value higher than max is min) + Boolean nonLinear; // are the values reported non-linear relative to element movement + Boolean preferredState; // does element have a preferred state (such as a button) + Boolean nullState; // does element have null state +*/ + + /* runtime variables used for auto-calibration */ + long minReport; // min returned value + long maxReport; // max returned value + + struct recElement * pNext; // next element in list +}; +typedef struct recElement recElement; + +struct joystick_hwdata +{ + IOHIDDeviceInterface ** interface; // interface to device, NULL = no interface + + char product[256]; // name of product + long usage; // usage page from IOUSBHID Parser.h which defines general usage + long usagePage; // usage within above page from IOUSBHID Parser.h which defines specific usage + + long axes; // number of axis (calculated, not reported by device) + long buttons; // number of buttons (calculated, not reported by device) + long hats; // number of hat switches (calculated, not reported by device) + long elements; // number of total elements (shouldbe total of above) (calculated, not reported by device) + + recElement* firstAxis; + recElement* firstButton; + recElement* firstHat; + + struct recDevice* pNext; // next device +}; +typedef struct joystick_hwdata recDevice; + + +/* Linked list of all available devices */ +static recDevice *gpDeviceList = NULL; + + +void HIDReportErrorNum (char * strError, long numError) +{ + SDL_SetError(strError); +} + +static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, recDevice *pDevice); + +/* returns current value for element, polling element + * will return 0 on error conditions which should be accounted for by application + */ + +SInt32 HIDGetElementValue (recDevice *pDevice, recElement *pElement) +{ + IOReturn result = kIOReturnSuccess; + IOHIDEventStruct hidEvent; + hidEvent.value = 0; + + if (NULL != pDevice && NULL != pElement && NULL != pDevice->interface) + { + result = (*(pDevice->interface))->getElementValue(pDevice->interface, pElement->cookie, &hidEvent); + if (kIOReturnSuccess == result) + { + /* record min and max for auto calibration */ + if (hidEvent.value < pElement->minReport) + pElement->minReport = hidEvent.value; + if (hidEvent.value > pElement->maxReport) + pElement->maxReport = hidEvent.value; + } + } + + // auto user scale + return hidEvent.value; +} + +/* similiar to HIDGetElementValue, but auto-calibrates the value before returning it */ + +SInt32 HIDCalibratedValue (recDevice *pDevice, recElement *pElement) +{ + float deviceScale = pElement->max - pElement->min; + float readScale = pElement->maxReport - pElement->minReport; + SInt32 value = HIDGetElementValue(pDevice, pElement); + if (readScale == 0) + return value; // no scaling at all + else + return ((value - pElement->minReport) * deviceScale / readScale) + pElement->min; +} + +/* similiar to HIDCalibratedValue but calibrates to an arbitrary scale instead of the elements default scale */ + +SInt32 HIDScaledCalibratedValue (recDevice *pDevice, recElement *pElement, long min, long max) +{ + float deviceScale = max - min; + float readScale = pElement->maxReport - pElement->minReport; + SInt32 value = HIDGetElementValue(pDevice, pElement); + if (readScale == 0) + return value; // no scaling at all + else + return ((value - pElement->minReport) * deviceScale / readScale) + min; +} + +/* Create and open an interface to device, required prior to extracting values or building queues. + * Note: appliction now owns the device and must close and release it prior to exiting + */ + +IOReturn HIDCreateOpenDeviceInterface (io_object_t hidDevice, recDevice *pDevice) +{ + IOReturn result = kIOReturnSuccess; + HRESULT plugInResult = S_OK; + SInt32 score = 0; + IOCFPlugInInterface ** ppPlugInInterface = NULL; + + if (NULL == pDevice->interface) + { + result = IOCreatePlugInInterfaceForService (hidDevice, kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &ppPlugInInterface, &score); + if (kIOReturnSuccess == result) + { + // Call a method of the intermediate plug-in to create the device interface + plugInResult = (*ppPlugInInterface)->QueryInterface (ppPlugInInterface, + CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), (void *) &(pDevice->interface)); + if (S_OK != plugInResult) + HIDReportErrorNum ("CouldnŐt query HID class device interface from plugInInterface", plugInResult); + (*ppPlugInInterface)->Release (ppPlugInInterface); + } + else + HIDReportErrorNum ("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.", result); + } + if (NULL != pDevice->interface) + { + result = (*(pDevice->interface))->open (pDevice->interface, 0); + if (kIOReturnSuccess != result) + HIDReportErrorNum ("Failed to open pDevice->interface via open.", result); + } + return result; +} + +/* Closes and releases interface to device, should be done prior to exting application + * Note: will have no affect if device or interface do not exist + * application will "own" the device if interface is not closed + * (device may have to be plug and re-plugged in different location to get it working again without a restart) + */ + +IOReturn HIDCloseReleaseInterface (recDevice *pDevice) +{ + IOReturn result = kIOReturnSuccess; + + if ((NULL != pDevice) && (NULL != pDevice->interface)) + { + // close the interface + result = (*(pDevice->interface))->close (pDevice->interface); + if (kIOReturnNotOpen == result) + { + // do nothing as device was not opened, thus can't be closed + } + else if (kIOReturnSuccess != result) + HIDReportErrorNum ("Failed to close IOHIDDeviceInterface.", result); + //release the interface + result = (*(pDevice->interface))->Release (pDevice->interface); + if (kIOReturnSuccess != result) + HIDReportErrorNum ("Failed to release IOHIDDeviceInterface.", result); + pDevice->interface = NULL; + } + return result; +} + +/* extracts actual specific element information from each element CF dictionary entry */ + +static void HIDGetElementInfo (CFTypeRef refElement, recElement *pElement) +{ + long number; + CFTypeRef refType; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementCookieKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->cookie = (IOHIDElementCookie) number; + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMinKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->min = number; + pElement->maxReport = pElement->min; + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMaxKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->max = number; + pElement->minReport = pElement->max; +/* + TODO: maybe should handle the following stuff somehow? + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMinKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->scaledMin = number; + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMaxKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->scaledMax = number; + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementSizeKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->size = number; + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsRelativeKey)); + if (refType) + pElement->relative = CFBooleanGetValue (refType); + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey)); + if (refType) + pElement->wrapping = CFBooleanGetValue (refType); + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey)); + if (refType) + pElement->nonLinear = CFBooleanGetValue (refType); + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey)); + if (refType) + pElement->preferredState = CFBooleanGetValue (refType); + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey)); + if (refType) + pElement->nullState = CFBooleanGetValue (refType); +*/ +} + +/* examines CF dictionary vlaue in device element hierarchy to determine if it is element of interest or a collection of more elements + * if element of interest allocate storage, add to list and retrieve element specific info + * if collection then pass on to deconstruction collection into additional individual elements + */ + +static void HIDAddElement (CFTypeRef refElement, recDevice* pDevice) +{ + recElement* element = NULL; + recElement** headElement = NULL; + long elementType, usagePage, usage; + CFTypeRef refElementType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementTypeKey)); + CFTypeRef refUsagePage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsagePageKey)); + CFTypeRef refUsage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsageKey)); + + + if ((refElementType) && (CFNumberGetValue (refElementType, kCFNumberLongType, &elementType))) + { + /* look at types of interest */ + if ((elementType == kIOHIDElementTypeInput_Misc) || (elementType == kIOHIDElementTypeInput_Button) || + (elementType == kIOHIDElementTypeInput_Axis)) + { + if (refUsagePage && CFNumberGetValue (refUsagePage, kCFNumberLongType, &usagePage) && + refUsage && CFNumberGetValue (refUsage, kCFNumberLongType, &usage)) + { + switch (usagePage) /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */ + { + case kHIDPage_GenericDesktop: + { + switch (usage) /* look at usage to determine function */ + { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + element = (recElement *) NewPtrClear (sizeof (recElement)); + if (element) + { + pDevice->axes++; + headElement = &(pDevice->firstAxis); + } + break; + case kHIDUsage_GD_Hatswitch: + element = (recElement *) NewPtrClear (sizeof (recElement)); + if (element) + { + pDevice->hats++; + headElement = &(pDevice->firstHat); + } + break; + } + } + break; + case kHIDPage_Button: + element = (recElement *) NewPtrClear (sizeof (recElement)); + if (element) + { + pDevice->buttons++; + headElement = &(pDevice->firstButton); + } + break; + default: + break; + } + } + } + else if (kIOHIDElementTypeCollection == elementType) + HIDGetCollectionElements ((CFMutableDictionaryRef) refElement, pDevice); + } + + if (element && headElement) /* add to list */ + { + pDevice->elements++; + if (NULL == *headElement) + *headElement = element; + else + { + recElement *elementPrevious, *elementCurrent; + elementCurrent = *headElement; + while (elementCurrent) + { + elementPrevious = elementCurrent; + elementCurrent = elementPrevious->pNext; + } + elementPrevious->pNext = element; + } + element->pNext = NULL; + HIDGetElementInfo (refElement, element); + } +} + +/* collects information from each array member in device element list (each array memeber = element) */ + +static void HIDGetElementsCFArrayHandler (const void * value, void * parameter) +{ + if (CFGetTypeID (value) == CFDictionaryGetTypeID ()) + HIDAddElement ((CFTypeRef) value, (recDevice *) parameter); +} + +/* handles retrieval of element information from arrays of elements in device IO registry information */ + +static void HIDGetElements (CFTypeRef refElementCurrent, recDevice *pDevice) +{ + CFTypeID type = CFGetTypeID (refElementCurrent); + if (type == CFArrayGetTypeID()) /* if element is an array */ + { + CFRange range = {0, CFArrayGetCount (refElementCurrent)}; + /* CountElementsCFArrayHandler called for each array member */ + CFArrayApplyFunction (refElementCurrent, range, HIDGetElementsCFArrayHandler, pDevice); + } +} + +/* handles extracting element information from element collection CF types + * used from top level element decoding and hierarchy deconstruction to flatten device element list + */ + +static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, recDevice *pDevice) +{ + CFTypeRef refElementTop = CFDictionaryGetValue (deviceProperties, CFSTR(kIOHIDElementKey)); + if (refElementTop) + HIDGetElements (refElementTop, pDevice); +} + +/* use top level element usage page and usage to discern device usage page and usage setting appropriate vlaues in device record */ + +static void HIDTopLevelElementHandler (const void * value, void * parameter) +{ + CFTypeRef refCF = 0; + if (CFGetTypeID (value) != CFDictionaryGetTypeID ()) + return; + refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsagePageKey)); + if (!CFNumberGetValue (refCF, kCFNumberLongType, &((recDevice *) parameter)->usagePage)) + SDL_SetError ("CFNumberGetValue error retrieving pDevice->usagePage."); + refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsageKey)); + if (!CFNumberGetValue (refCF, kCFNumberLongType, &((recDevice *) parameter)->usage)) + SDL_SetError ("CFNumberGetValue error retrieving pDevice->usage."); +} + +/* extracts device info from CF dictionary records in IO registry */ + +static void HIDGetDeviceInfo (io_object_t hidDevice, CFMutableDictionaryRef hidProperties, recDevice *pDevice) +{ + CFMutableDictionaryRef usbProperties = 0; + io_registry_entry_t parent1, parent2; + + /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also + * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties + */ + if ((KERN_SUCCESS == IORegistryEntryGetParentEntry (hidDevice, kIOServicePlane, &parent1)) && + (KERN_SUCCESS == IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2)) && + (KERN_SUCCESS == IORegistryEntryCreateCFProperties (parent2, &usbProperties, kCFAllocatorDefault, kNilOptions))) + { + if (usbProperties) + { + CFTypeRef refCF = 0; + /* get device info + * try hid dictionary first, if fail then go to usb dictionary + */ + + + /* get product name */ + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductKey)); + if (!refCF) + refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Product Name")); + if (refCF) + { + if (!CFStringGetCString (refCF, pDevice->product, 256, CFStringGetSystemEncoding ())) + SDL_SetError ("CFStringGetCString error retrieving pDevice->product."); + } + + /* get usage page and usage */ + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey)); + if (refCF) + { + if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usagePage)) + SDL_SetError ("CFNumberGetValue error retrieving pDevice->usagePage."); + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsageKey)); + if (refCF) + if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usage)) + SDL_SetError ("CFNumberGetValue error retrieving pDevice->usage."); + } + + if (NULL == refCF) /* get top level element HID usage page or usage */ + { + /* use top level element instead */ + CFTypeRef refCFTopElement = 0; + refCFTopElement = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDElementKey)); + { + /* refCFTopElement points to an array of element dictionaries */ + CFRange range = {0, CFArrayGetCount (refCFTopElement)}; + CFArrayApplyFunction (refCFTopElement, range, HIDTopLevelElementHandler, pDevice); + } + } + + CFRelease (usbProperties); + } + else + SDL_SetError ("IORegistryEntryCreateCFProperties failed to create usbProperties."); + + if (kIOReturnSuccess != IOObjectRelease (parent2)) + SDL_SetError ("IOObjectRelease error with parent2."); + if (kIOReturnSuccess != IOObjectRelease (parent1)) + SDL_SetError ("IOObjectRelease error with parent1."); + } +} + + +static recDevice *HIDBuildDevice (io_object_t hidDevice) +{ + recDevice *pDevice = (recDevice *) NewPtrClear (sizeof (recDevice)); + if (pDevice) + { + /* get dictionary for HID properties */ + CFMutableDictionaryRef hidProperties = 0; + kern_return_t result = IORegistryEntryCreateCFProperties (hidDevice, &hidProperties, kCFAllocatorDefault, kNilOptions); + if ((result == KERN_SUCCESS) && hidProperties) + { + /* create device interface */ + result = HIDCreateOpenDeviceInterface (hidDevice, pDevice); + if (kIOReturnSuccess == result) + { + HIDGetDeviceInfo (hidDevice, hidProperties, pDevice); /* hidDevice used to find parents in registry tree */ + HIDGetCollectionElements (hidProperties, pDevice); + } + else + { + DisposePtr(pDevice); + pDevice = NULL; + } + CFRelease (hidProperties); + } + else + { + DisposePtr(pDevice); + pDevice = NULL; + } + } + return pDevice; +} + +/* disposes of the element list associated with a device and the memory associated with the list + */ + +static void HIDDisposeElementList (recElement **elementList) +{ + recElement *pElement = *elementList; + while (pElement) + { + recElement *pElementNext = pElement->pNext; + DisposePtr ((Ptr) pElement); + pElement = pElementNext; + } + *elementList = NULL; +} + +/* disposes of a single device, closing and releaseing interface, freeing memory fro device and elements, setting device pointer to NULL + * all your device no longer belong to us... (i.e., you do not 'own' the device anymore) + */ + +static recDevice *HIDDisposeDevice (recDevice **ppDevice) +{ + kern_return_t result = KERN_SUCCESS; + recDevice *pDeviceNext = NULL; + if (*ppDevice) + { + // save next device prior to disposing of this device + pDeviceNext = (*ppDevice)->pNext; + + /* free element lists */ + HIDDisposeElementList (&(*ppDevice)->firstAxis); + HIDDisposeElementList (&(*ppDevice)->firstButton); + HIDDisposeElementList (&(*ppDevice)->firstHat); + + result = HIDCloseReleaseInterface (*ppDevice); /* function sanity checks interface value (now application does not own device) */ + if (kIOReturnSuccess != result) + HIDReportErrorNum ("HIDCloseReleaseInterface failed when trying to dipose device.", result); + DisposePtr ((Ptr)*ppDevice); + *ppDevice = NULL; + } + return pDeviceNext; +} + + +/* Function to scan the system for joysticks. + * Joystick 0 should be the system default joystick. + * This function should return the number of available joysticks, or -1 + * on an unrecoverable fatal error. + */ +int SDL_SYS_JoystickInit(void) +{ + IOReturn result = kIOReturnSuccess; + mach_port_t masterPort = NULL; + io_iterator_t hidObjectIterator = NULL; + CFMutableDictionaryRef hidMatchDictionary = NULL; + recDevice *device, *lastDevice; + io_object_t ioHIDDeviceObject = NULL; + UInt32 usagePage = kHIDPage_GenericDesktop; + UInt32 usage = kHIDUsage_GD_Joystick; /* We probably also should check for gamepads? */ + + SDL_numjoysticks = 0; + + if (NULL != gpDeviceList) + { + SDL_SetError("Joystick: Device list already inited."); + return -1; + } + + result = IOMasterPort (bootstrap_port, &masterPort); + if (kIOReturnSuccess != result) + { + SDL_SetError("Joystick: IOMasterPort error with bootstrap_port."); + return -1; + } + + /* Set up a matching dictionary to search I/O Registry by class name for all HID class devices. */ + hidMatchDictionary = IOServiceMatching (kIOHIDDeviceKey); + if ((hidMatchDictionary != NULL) && (usagePage) && (usage)) + { + /* Add key for device type (joystick, in this case) to refine the matching dictionary. */ + CFNumberRef refUsage = NULL, refUsagePage = NULL; + + refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usagePage); + CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage); + refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usage); + CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage); + } + else + { + SDL_SetError("Joystick: Failed to get HID CFMutableDictionaryRef via IOServiceMatching."); + return -1; + } + + /*/ Now search I/O Registry for matching devices. */ + result = IOServiceGetMatchingServices (masterPort, hidMatchDictionary, &hidObjectIterator); + /* Check for errors */ + if ((kIOReturnSuccess != result) || (NULL == hidObjectIterator)) + { + SDL_SetError("Joystick: Couldn't create a HID object iterator."); + return -1; + } + /* IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. */ + + /* build flat linked list of devices from device iterator */ + + gpDeviceList = lastDevice = NULL; + + while ((ioHIDDeviceObject = IOIteratorNext (hidObjectIterator))) + { + /* build a device record */ + device = HIDBuildDevice (ioHIDDeviceObject); + if (!device) + continue; + + /* dump device object, it is no longer needed */ + result = IOObjectRelease (ioHIDDeviceObject); +// if (KERN_SUCCESS != result) +// HIDReportErrorNum ("IOObjectRelease error with ioHIDDeviceObject.", result); + /* Add device to the end of the list */ + if (lastDevice) + lastDevice->pNext = device; + else + gpDeviceList = device; + lastDevice = device; + } + result = IOObjectRelease (hidObjectIterator); /* release the iterator */ + + /* Count the total number of devices we found */ + device = gpDeviceList; + while (device) + { + SDL_numjoysticks++; + device = device->pNext; + } + + return SDL_numjoysticks; +} + +/* Function to get the device-dependent name of a joystick */ +const char *SDL_SYS_JoystickName(int index) +{ + recDevice *device = gpDeviceList; + + for (; index > 0; index--) + device = device->pNext; + + return device->product; +} + +/* Function to open a joystick for use. + * The joystick to open is specified by the index field of the joystick. + * This should fill the nbuttons and naxes fields of the joystick structure. + * It returns 0, or -1 if there is an error. + */ +int SDL_SYS_JoystickOpen(SDL_Joystick *joystick) +{ + recDevice *device = gpDeviceList; + int index; + + for (index = joystick->index; index > 0; index--) + device = device->pNext; + + joystick->hwdata = device; + joystick->name = device->product; + + joystick->naxes = device->axes; + joystick->nhats = device->hats; + joystick->nballs = 0; + joystick->nbuttons = device->buttons; + + return 0; +} + +/* Function to update the state of a joystick - called as a device poll. + * This function shouldn't update the joystick structure directly, + * but instead should call SDL_PrivateJoystick*() to deliver events + * and update joystick device state. + */ +void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick) +{ + recDevice *device = joystick->hwdata; + recElement *element; + SInt32 value; + int i; + + element = device->firstAxis; + i = 0; + while (element) + { + value = HIDScaledCalibratedValue(device, element, -32768, 32767); + if ( value != joystick->axes[i] ) + SDL_PrivateJoystickAxis(joystick, i, value); + element = element->pNext; + ++i; + } + + element = device->firstButton; + i = 0; + while (element) + { + value = HIDGetElementValue(device, element); + if ( value != joystick->buttons[i] ) + SDL_PrivateJoystickButton(joystick, i, value); + element = element->pNext; + ++i; + } + + element = device->firstHat; + i = 0; + while (element) + { + Uint8 pos; + + value = HIDGetElementValue(device, element); + switch(value) + { + case 0: + pos = SDL_HAT_CENTERED; + break; + case 1: + pos = SDL_HAT_UP; + break; + case 2: + pos = SDL_HAT_RIGHTUP; + break; + case 3: + pos = SDL_HAT_RIGHT; + break; + case 4: + pos = SDL_HAT_RIGHTDOWN; + break; + case 5: + pos = SDL_HAT_DOWN; + break; + case 6: + pos = SDL_HAT_LEFTDOWN; + break; + case 7: + pos = SDL_HAT_LEFT; + break; + case 8: + pos = SDL_HAT_LEFTUP; + break; + } + if ( pos != joystick->hats[i] ) + SDL_PrivateJoystickHat(joystick, i, pos); + element = element->pNext; + ++i; + } + + return; +} + +/* Function to close a joystick after use */ +void SDL_SYS_JoystickClose(SDL_Joystick *joystick) +{ + /* Should we do anything here? */ + return; +} + +/* Function to perform any system-specific joystick related cleanup */ +void SDL_SYS_JoystickQuit(void) +{ + while (NULL != gpDeviceList) + gpDeviceList = HIDDisposeDevice (&gpDeviceList); +} diff --git a/src/video/quartz/SDL_QuartzEvents.m b/src/video/quartz/SDL_QuartzEvents.m index 79ea3930a..2c12d532e 100644 --- a/src/video/quartz/SDL_QuartzEvents.m +++ b/src/video/quartz/SDL_QuartzEvents.m @@ -1,37 +1,43 @@ /* - SDL - Simple DirectMedia Layer - Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Sam Lantinga - slouken@devolution.com + Sam Lantinga + slouken@devolution.com */ #include "SDL_QuartzKeys.h" -static int last_virtual_button = 0; // Last virtual mouse button pressed +static SDLKey keymap[256]; +static unsigned int currentMods = 0; /* Current keyboard modifiers, to track modifier state */ +static int last_virtual_button = 0; /* Last virtual mouse button pressed */ -static void QZ_InitOSKeymap (_THIS) { +static void QZ_InitOSKeymap (_THIS) { + const void *KCHRPtr; + UInt32 state; + UInt32 value; int i; - + int world = SDLK_WORLD_0; + for ( i=0; ikeysym map. However, it will not + * work very well on international keyboard. Hence we now query MacOS + * for its own keymap to adjust our own mapping table. However, this is + * bascially only useful for ascii char keys. This is also the reason + * why we keep the static table, too. + */ + + /* Get a pointer to the systems cached KCHR */ + KCHRPtr = (void *)GetScriptManagerVariable(smKCHRCache); + if (KCHRPtr) + { + /* Loop over all 127 possible scan codes */ + for (i = 0; i < 0x7F; i++) + { + /* We pretend a clean start to begin with (i.e. no dead keys active */ + state = 0; + + /* Now translate the key code to a key value */ + value = KeyTranslate(KCHRPtr, i, &state) & 0xff; + + /* If the state become 0, it was a dead key. We need to translate again, + passing in the new state, to get the actual key value */ + if (state != 0) + value = KeyTranslate(KCHRPtr, i, &state) & 0xff; + + /* Now we should have an ascii value, or 0. Try to figure out to which SDL symbol it maps */ + if (value >= 128) /* Some non-ASCII char, map it to SDLK_WORLD_* */ + keymap[i] = world++; + else if (value >= 32) /* non-control ASCII char */ + keymap[i] = value; + } + } + + /* The keypad codes are re-setup here, because the loop above cannot + * distinguish between a key on the keypad and a regular key. We maybe + * could get around this problem in another fashion: NSEvent's flags + * include a "NSNumericPadKeyMask" bit; we could check that and modify + * the symbol we return on the fly. However, this flag seems to exhibit + * some weird behaviour related to the num lock key + */ + keymap[QZ_KP0] = SDLK_KP0; + keymap[QZ_KP1] = SDLK_KP1; + keymap[QZ_KP2] = SDLK_KP2; + keymap[QZ_KP3] = SDLK_KP3; + keymap[QZ_KP4] = SDLK_KP4; + keymap[QZ_KP5] = SDLK_KP5; + keymap[QZ_KP6] = SDLK_KP6; + keymap[QZ_KP7] = SDLK_KP7; + keymap[QZ_KP8] = SDLK_KP8; + keymap[QZ_KP9] = SDLK_KP9; + keymap[QZ_KP_MINUS] = SDLK_KP_MINUS; + keymap[QZ_KP_PLUS] = SDLK_KP_PLUS; + keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD; + keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS; + keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE; + keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY; + keymap[QZ_KP_ENTER] = SDLK_KP_ENTER; } static void QZ_DoKey (int state, NSEvent *event) { - NSString *chars; - int i; - SDL_keysym key; - - /* An event can contain multiple characters */ - /* I'll ignore this fact for now, since there is only one virtual key code per event */ - chars = [ event characters ]; - for (i =0; i < 1 /*[ chars length ] */; i++) { - - key.scancode = [ event keyCode ]; - key.sym = keymap [ key.scancode ]; - key.unicode = [ chars characterAtIndex:i]; - key.mod = KMOD_NONE; - - SDL_PrivateKeyboard (state, &key); - } + NSString *chars; + int i; + SDL_keysym key; + + /* An event can contain multiple characters */ + /* I'll ignore this fact for now, since there is only one virtual key code per event */ + chars = [ event characters ]; + for (i =0; i < 1 /*[ chars length ] */; i++) { + + key.scancode = [ event keyCode ]; + key.sym = keymap [ key.scancode ]; + key.unicode = [ chars characterAtIndex:i]; + key.mod = KMOD_NONE; + + SDL_PrivateKeyboard (state, &key); + } } static void QZ_DoModifiers (unsigned int newMods) { - const int offset = 18; - const int mapping[] = { SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, 0, SDLK_LMETA } ; + const int mapping[] = { SDLK_CAPSLOCK, SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA } ; + + int i; + int bit; + SDL_keysym key; - int bit; - SDL_keysym key; - key.scancode = 0; - key.sym = SDLK_UNKNOWN; - key.unicode = 0; - key.mod = KMOD_NONE; - - /* Iterate through the bits, testing each against the current modifiers */ - for (bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1) { - - unsigned int currentMask, newMask; - - currentMask = currentMods & bit; - newMask = newMods & bit; - - if ( currentMask && - currentMask != newMask ) { /* modifier up event */ + key.scancode = 0; + key.sym = SDLK_UNKNOWN; + key.unicode = 0; + key.mod = KMOD_NONE; + + /* Iterate through the bits, testing each against the current modifiers */ + for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) { + + unsigned int currentMask, newMask; + + currentMask = currentMods & bit; + newMask = newMods & bit; + + if ( currentMask && + currentMask != newMask ) { /* modifier up event */ - key.sym = mapping[ currentMask >> offset ]; - SDL_PrivateKeyboard (SDL_RELEASED, &key); - } - else - if ( newMask && - currentMask != newMask ) { /* modifier down event */ - - key.sym = mapping [ newMask >> offset ]; - SDL_PrivateKeyboard (SDL_PRESSED, &key); - } - } - - currentMods = newMods; + key.sym = mapping[i]; + /* If this was Caps Lock, we need some additional voodoo to make SDL happy */ + if (bit == NSAlphaShiftKeyMask) + SDL_PrivateKeyboard (SDL_PRESSED, &key); + SDL_PrivateKeyboard (SDL_RELEASED, &key); + } + else + if ( newMask && + currentMask != newMask ) { /* modifier down event */ + + key.sym = mapping[i]; + SDL_PrivateKeyboard (SDL_PRESSED, &key); + /* If this was Caps Lock, we need some additional voodoo to make SDL happy */ + if (bit == NSAlphaShiftKeyMask) + SDL_PrivateKeyboard (SDL_RELEASED, &key); + } + } + + currentMods = newMods; } static void QZ_DoActivate (_THIS) { - inForeground = YES; + inForeground = YES; - /* Regrab the mouse */ - if (currentGrabMode == SDL_GRAB_ON) { - QZ_WarpWMCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2); - CGAssociateMouseAndMouseCursorPosition (0); - } + /* Regrab the mouse */ + if (currentGrabMode == SDL_GRAB_ON) { + QZ_WarpWMCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2); + CGAssociateMouseAndMouseCursorPosition (0); + } - /* Hide the mouse cursor if inside the app window */ - // FIXME - if (!QZ_cursor_visible) { - HideCursor (); - } - - SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS); + /* Hide the mouse cursor if inside the app window */ + if (!QZ_cursor_visible) { + HideCursor (); + } + + SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS); } static void QZ_DoDeactivate (_THIS) { - - inForeground = NO; + + inForeground = NO; - /* Ungrab mouse if it is grabbed */ - if (currentGrabMode == SDL_GRAB_ON) { - CGAssociateMouseAndMouseCursorPosition (1); - } + /* Ungrab mouse if it is grabbed */ + if (currentGrabMode == SDL_GRAB_ON) { + CGAssociateMouseAndMouseCursorPosition (1); + } - /* Show the mouse cursor */ - // FIXME - if (!QZ_cursor_visible) { - ShowCursor (); - } - - SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS); + /* Show the mouse cursor */ + if (!QZ_cursor_visible) { + ShowCursor (); + } + + SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS); } static void QZ_PumpEvents (_THIS) { - NSDate *distantPast; - NSEvent *event; - NSRect winRect; - NSRect titleBarRect; - NSAutoreleasePool *pool; - - distantPast = [ [ NSDate distantPast ] retain ]; - - pool = [ [ NSAutoreleasePool alloc ] init ]; - - winRect = NSMakeRect (0, 0, SDL_VideoSurface->w + 1, SDL_VideoSurface->h + 1); - titleBarRect = NSMakeRect ( 0, SDL_VideoSurface->h, SDL_VideoSurface->w, - SDL_VideoSurface->h + 22 ); - - do { - - /* Poll for an event. This will not block */ - event = [ NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:distantPast - inMode: NSDefaultRunLoopMode dequeue:YES ]; - - if (event != nil) { - unsigned int type; - BOOL isForGameWin; + NSDate *distantPast; + NSEvent *event; + NSRect winRect; + NSRect titleBarRect; + NSAutoreleasePool *pool; + + pool = [ [ NSAutoreleasePool alloc ] init ]; + distantPast = [ NSDate distantPast ]; + + winRect = NSMakeRect (0, 0, SDL_VideoSurface->w + 1, SDL_VideoSurface->h + 1); + titleBarRect = NSMakeRect ( 0, SDL_VideoSurface->h, SDL_VideoSurface->w, + SDL_VideoSurface->h + 22 ); + + do { + + /* Poll for an event. This will not block */ + event = [ NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:distantPast + inMode: NSDefaultRunLoopMode dequeue:YES ]; + + if (event != nil) { + unsigned int type; + BOOL isForGameWin; - #define DO_MOUSE_DOWN(button, sendToWindow) do { \ - if ( inForeground ) { \ - if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \ - NSPointInRect([event locationInWindow], winRect) ) \ - SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); \ - } \ - else { \ - QZ_DoActivate (this); \ - } \ - [ NSApp sendEvent:event ]; \ - } while(0) - - #define DO_MOUSE_UP(button, sendToWindow) do { \ - if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \ - !NSPointInRect([event locationInWindow], titleBarRect) )\ - SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0); \ - [ NSApp sendEvent:event ]; \ - } while(0) + #define DO_MOUSE_DOWN(button, sendToWindow) do { \ + if ( inForeground ) { \ + if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \ + NSPointInRect([event locationInWindow], winRect) ) \ + SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); \ + } \ + else { \ + QZ_DoActivate (this); \ + } \ + [ NSApp sendEvent:event ]; \ + } while(0) + + #define DO_MOUSE_UP(button, sendToWindow) do { \ + if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \ + !NSPointInRect([event locationInWindow], titleBarRect) )\ + SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0); \ + [ NSApp sendEvent:event ]; \ + } while(0) - type = [ event type ]; - isForGameWin = (qz_window == [ event window ]); - switch (type) { - - case NSLeftMouseDown: - if ( NSCommandKeyMask & currentMods ) { - last_virtual_button = 3; - DO_MOUSE_DOWN (3, 0); - } - else if ( NSAlternateKeyMask & currentMods ) { - last_virtual_button = 2; - DO_MOUSE_DOWN (2, 0); - } - else { - DO_MOUSE_DOWN (1, 1); - } - break; - case 25: DO_MOUSE_DOWN (2, 0); break; - case NSRightMouseDown: DO_MOUSE_DOWN (3, 0); break; - case NSLeftMouseUp: - - if ( last_virtual_button != 0 ) { - DO_MOUSE_UP (last_virtual_button, 0); - last_virtual_button = 0; - } - else { - DO_MOUSE_UP (1, 1); - } - break; - case 26: DO_MOUSE_UP (2, 0); break; - case NSRightMouseUp: DO_MOUSE_UP (3, 0); break; - case NSSystemDefined: - //if ([event subtype] == 7) { - // unsigned int buttons; // up to 32 mouse button states! - // buttons = [ event data2 ]; - //} - break; - case NSLeftMouseDragged: - case NSRightMouseDragged: - case 27: - case NSMouseMoved: - if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) - || NSPointInRect([event locationInWindow], winRect) ) - { - static int moves = 0; - NSPoint p; - - if ( SDL_VideoSurface->flags & SDL_FULLSCREEN ) { - p = [ NSEvent mouseLocation ]; - p.y = [[NSScreen mainScreen] frame].size.height - p.y; - } else { - p = [ event locationInWindow ]; - p.y = SDL_VideoSurface->h - p.y; - } + type = [ event type ]; + isForGameWin = (qz_window == [ event window ]); + switch (type) { + + case NSLeftMouseDown: + if ( NSCommandKeyMask & currentMods ) { + last_virtual_button = 3; + DO_MOUSE_DOWN (3, 0); + } + else if ( NSAlternateKeyMask & currentMods ) { + last_virtual_button = 2; + DO_MOUSE_DOWN (2, 0); + } + else { + DO_MOUSE_DOWN (1, 1); + } + break; + case 25: DO_MOUSE_DOWN (2, 0); break; + case NSRightMouseDown: DO_MOUSE_DOWN (3, 0); break; + case NSLeftMouseUp: + + if ( last_virtual_button != 0 ) { + DO_MOUSE_UP (last_virtual_button, 0); + last_virtual_button = 0; + } + else { + DO_MOUSE_UP (1, 1); + } + break; + case 26: DO_MOUSE_UP (2, 0); break; + case NSRightMouseUp: DO_MOUSE_UP (3, 0); break; + case NSSystemDefined: + //if ([event subtype] == 7) { + // unsigned int buttons; // up to 32 mouse button states! + // buttons = [ event data2 ]; + //} + break; + case NSLeftMouseDragged: + case NSRightMouseDragged: + case 27: + case NSMouseMoved: + if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) + || NSPointInRect([event locationInWindow], winRect) ) + { + static int moves = 0; + NSPoint p; + + if ( SDL_VideoSurface->flags & SDL_FULLSCREEN ) { + p = [ NSEvent mouseLocation ]; + p.y = [[NSScreen mainScreen] frame].size.height - p.y; + } else { + p = [ event locationInWindow ]; + p.y = SDL_VideoSurface->h - p.y; + } - if ( (moves % 10) == 0 ) { - SDL_PrivateMouseMotion (0, 0, p.x, p.y); - } - else { - CGMouseDelta dx, dy; - CGGetLastMouseDelta (&dx, &dy); - SDL_PrivateMouseMotion (0, 1, dx, dy); - } - moves++; - } - break; - case NSScrollWheel: - { - if (NSPointInRect([ event locationInWindow ], winRect)) { - float dy; - dy = [ event deltaY ]; - if ( dy > 0.0 ) /* Scroll up */ - SDL_PrivateMouseButton (SDL_PRESSED, 4, 0, 0); - else /* Scroll down */ - SDL_PrivateMouseButton (SDL_PRESSED, 5, 0, 0); - } - } - break; - case NSKeyUp: - QZ_DoKey (SDL_RELEASED, event); - break; - case NSKeyDown: - QZ_DoKey (SDL_PRESSED, event); - break; - case NSFlagsChanged: - QZ_DoModifiers( [ event modifierFlags ] ); - break; -// case NSMouseEntered: break; -// case NSMouseExited: break; - case NSAppKitDefined: - switch ( [ event subtype ] ) { - case NSApplicationActivatedEventType: - QZ_DoActivate (this); - break; - case NSApplicationDeactivatedEventType: - QZ_DoDeactivate (this); - break; - } - [ NSApp sendEvent:event ]; - break; -// case NSApplicationDefined: break; -// case NSPeriodic: break; -// case NSCursorUpdate: break; - default: - [ NSApp sendEvent:event ]; - } - } - } while (event != nil); - - [ pool release ]; - [ distantPast release ]; + if ( (moves % 10) == 0 ) { + SDL_PrivateMouseMotion (0, 0, p.x, p.y); + } + else { + CGMouseDelta dx, dy; + CGGetLastMouseDelta (&dx, &dy); + SDL_PrivateMouseMotion (0, 1, dx, dy); + } + moves++; + } + break; + case NSScrollWheel: + { + if (NSPointInRect([ event locationInWindow ], winRect)) { + float dy; + dy = [ event deltaY ]; + if ( dy > 0.0 ) /* Scroll up */ + SDL_PrivateMouseButton (SDL_PRESSED, 4, 0, 0); + else /* Scroll down */ + SDL_PrivateMouseButton (SDL_PRESSED, 5, 0, 0); + } + } + break; + case NSKeyUp: + QZ_DoKey (SDL_RELEASED, event); + break; + case NSKeyDown: + QZ_DoKey (SDL_PRESSED, event); + break; + case NSFlagsChanged: + QZ_DoModifiers( [ event modifierFlags ] ); + break; + /* case NSMouseEntered: break; */ + /* case NSMouseExited: break; */ + case NSAppKitDefined: + switch ( [ event subtype ] ) { + case NSApplicationActivatedEventType: + QZ_DoActivate (this); + break; + case NSApplicationDeactivatedEventType: + QZ_DoDeactivate (this); + break; + } + [ NSApp sendEvent:event ]; + break; + /* case NSApplicationDefined: break; */ + /* case NSPeriodic: break; */ + /* case NSCursorUpdate: break; */ + default: + [ NSApp sendEvent:event ]; + } + } + } while (event != nil); + + [ pool release ]; } diff --git a/src/video/quartz/SDL_QuartzVideo.m b/src/video/quartz/SDL_QuartzVideo.m index 91ce8e31c..3dd64f316 100644 --- a/src/video/quartz/SDL_QuartzVideo.m +++ b/src/video/quartz/SDL_QuartzVideo.m @@ -25,8 +25,6 @@ /* Some variables to share among files, put in device structure eventually */ static SDL_GrabMode currentGrabMode = SDL_GRAB_OFF; static BOOL inForeground = YES; -static SDLKey keymap[256]; -static unsigned int currentMods = 0; /* Current keyboard modifiers, to track modifier state */ static char QZ_Error[255]; /* Global error buffer to temporarily store more informative error messages */ /* Include files into one compile unit...break apart eventually */ diff --git a/test/acinclude.m4 b/test/acinclude.m4 index d2a478594..294942364 100644 --- a/test/acinclude.m4 +++ b/test/acinclude.m4 @@ -32,6 +32,7 @@ AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run fi fi + AC_REQUIRE([AC_CANONICAL_TARGET]) AC_PATH_PROG(SDL_CONFIG, sdl-config, no) min_sdl_version=ifelse([$1], ,0.11.0,$1) AC_MSG_CHECKING(for SDL - version >= $min_sdl_version)