Commit bea3cb1d authored by Sam Lantinga's avatar Sam Lantinga

Date: Sat, 8 Sep 2001 04:42:23 +0200

From: Max Horn <max@quendi.de>
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)

--HG--
extra : convert_revision : svn%3Ac70aab31-4412-0410-b14c-859654838e24/trunk%40173
parent 2d9275e4
...@@ -68,9 +68,9 @@ MacOS: ...@@ -68,9 +68,9 @@ MacOS:
Not all of the keys are properly recognized on the keyboard. Not all of the keys are properly recognized on the keyboard.
MacOS X: 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. Window may not close when unsetting video mode and resetting.
...@@ -98,8 +98,6 @@ MacOS X: ...@@ -98,8 +98,6 @@ MacOS X:
cursor in the center of the window/screen. Also, mouse moved events cursor in the center of the window/screen. Also, mouse moved events
are not generated, and the keyboard cannot be grabbed. 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. MacOS X seems to have a broken pthread_cancel() implementation.
FreeBSD: FreeBSD:
......
The latest development version of SDL is available via CVS: The latest development version of SDL is available via CVS:
cvs -d :pserver:guest@cvs.lokigames.com:/cvs login cvs -d :pserver:guest@libsdl.org:/home/slouken/libsdl.org/cvs login
# use the password "guest" # No password, so just hit enter when prompted for a password
cvs -d :pserver:guest@cvs.lokigames.com:/cvs checkout SDL 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 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 the files used by make by running the "autogen.sh" script, which will
run aclocal, automake, autoconf and then run configure. 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
...@@ -52,9 +52,12 @@ IV. Enjoy! :) ...@@ -52,9 +52,12 @@ IV. Enjoy! :)
If you have a project you'd like me to know about, or want to ask questions, 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: 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
============================================================================== ==============================================================================
...@@ -18,11 +18,7 @@ process: ...@@ -18,11 +18,7 @@ process:
(You may need to create the subdirs of /usr/local manually.) (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 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. framework", which is the port of the old Mac Toolbox to OS X.
To do this, use the -F and -framework arguments for compiling To do this, use the -F and -framework arguments for compiling
...@@ -33,6 +29,79 @@ and linking, respectively: ...@@ -33,6 +29,79 @@ and linking, respectively:
sdl-config knows about the linking path and -framework, so it's sdl-config knows about the linking path and -framework, so it's
recommended to use it to fill in your Makefile variables. 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 Using the Simple DirectMedia Layer with Project Builder
...@@ -122,3 +191,4 @@ following locations: ...@@ -122,3 +191,4 @@ following locations:
but I expect that things will still work on older versions. but I expect that things will still work on older versions.
Known bugs are listed in the file "BUGS" Known bugs are listed in the file "BUGS"
LocalWords: Stuffit
...@@ -2056,8 +2056,8 @@ case "$target" in ...@@ -2056,8 +2056,8 @@ case "$target" in
fi fi
# Set up files for the joystick library # Set up files for the joystick library
if test x$enable_joystick = xyes; then if test x$enable_joystick = xyes; then
JOYSTICK_SUBDIRS="$JOYSTICK_SUBDIRS dummy" JOYSTICK_SUBDIRS="$JOYSTICK_SUBDIRS darwin"
JOYSTICK_DRIVERS="$JOYSTICK_DRIVERS dummy/libjoystick_dummy.la" JOYSTICK_DRIVERS="$JOYSTICK_DRIVERS darwin/libjoystick_darwin.la"
fi fi
# Set up files for the cdrom library # Set up files for the cdrom library
if test x$enable_cdrom = xyes; then if test x$enable_cdrom = xyes; then
...@@ -2192,6 +2192,7 @@ include/Makefile ...@@ -2192,6 +2192,7 @@ include/Makefile
src/Makefile src/Makefile
src/main/Makefile src/main/Makefile
src/main/macosx/Makefile src/main/macosx/Makefile
src/main/macosx/Info.plist
src/audio/Makefile src/audio/Makefile
src/audio/alsa/Makefile src/audio/alsa/Makefile
src/audio/arts/Makefile src/audio/arts/Makefile
...@@ -2236,6 +2237,7 @@ src/events/Makefile ...@@ -2236,6 +2237,7 @@ src/events/Makefile
src/joystick/Makefile src/joystick/Makefile
src/joystick/amigaos/Makefile src/joystick/amigaos/Makefile
src/joystick/beos/Makefile src/joystick/beos/Makefile
src/joystick/darwin/Makefile
src/joystick/dummy/Makefile src/joystick/dummy/Makefile
src/joystick/linux/Makefile src/joystick/linux/Makefile
src/joystick/macos/Makefile src/joystick/macos/Makefile
......
...@@ -32,6 +32,7 @@ AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run ...@@ -32,6 +32,7 @@ AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run
fi fi
fi fi
AC_REQUIRE([AC_CANONICAL_TARGET])
AC_PATH_PROG(SDL_CONFIG, sdl-config, no) AC_PATH_PROG(SDL_CONFIG, sdl-config, no)
min_sdl_version=ifelse([$1], ,0.11.0,$1) min_sdl_version=ifelse([$1], ,0.11.0,$1)
AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) AC_MSG_CHECKING(for SDL - version >= $min_sdl_version)
......
...@@ -5,7 +5,7 @@ noinst_LTLIBRARIES = libjoystick.la ...@@ -5,7 +5,7 @@ noinst_LTLIBRARIES = libjoystick.la
# Define which subdirectories need to be built # Define which subdirectories need to be built
SUBDIRS = @JOYSTICK_SUBDIRS@ SUBDIRS = @JOYSTICK_SUBDIRS@
DIST_SUBDIRS = dummy amigaos beos linux macos win32 DIST_SUBDIRS = dummy amigaos beos darwin linux macos win32
DRIVERS = @JOYSTICK_DRIVERS@ DRIVERS = @JOYSTICK_DRIVERS@
......
Makefile.in
Makefile
.libs
*.o
*.lo
*.la
## 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
/*
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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/errno.h>
#include <sysexits.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/IOUSBHIDParser.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <CoreFoundation/CoreFoundation.h>
#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 ("Couldnt 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);
}
/* /*
SDL - Simple DirectMedia Layer SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version. version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details. Library General Public License for more details.
You should have received a copy of the GNU Library General Public You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga Sam Lantinga
slouken@devolution.com slouken@devolution.com
*/ */
#include "SDL_QuartzKeys.h" #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 i;
int world = SDLK_WORLD_0;
for ( i=0; i<SDL_TABLESIZE(keymap); ++i ) for ( i=0; i<SDL_TABLESIZE(keymap); ++i )
keymap[i] = SDLK_UNKNOWN; keymap[i] = SDLK_UNKNOWN;
// This keymap is almost exactly the same as the OS 9 one /* This keymap is almost exactly the same as the OS 9 one */
keymap[QZ_ESCAPE] = SDLK_ESCAPE; keymap[QZ_ESCAPE] = SDLK_ESCAPE;
keymap[QZ_F1] = SDLK_F1; keymap[QZ_F1] = SDLK_F1;
keymap[QZ_F2] = SDLK_F2; keymap[QZ_F2] = SDLK_F2;
keymap[QZ_F3] = SDLK_F3; keymap[QZ_F3] = SDLK_F3;
...@@ -135,261 +141,322 @@ static void QZ_InitOSKeymap (_THIS) { ...@@ -135,261 +141,322 @@ static void QZ_InitOSKeymap (_THIS) {
keymap[QZ_IBOOK_ENTER] = SDLK_KP_ENTER; keymap[QZ_IBOOK_ENTER] = SDLK_KP_ENTER;
keymap[QZ_IBOOK_RIGHT] = SDLK_RIGHT; keymap[QZ_IBOOK_RIGHT] = SDLK_RIGHT;
keymap[QZ_IBOOK_DOWN] = SDLK_DOWN; keymap[QZ_IBOOK_DOWN] = SDLK_DOWN;
keymap[QZ_IBOOK_UP] = SDLK_UP; keymap[QZ_IBOOK_UP] = SDLK_UP;
keymap[QZ_IBOOK_LEFT] = SDLK_LEFT; keymap[QZ_IBOOK_LEFT] = SDLK_LEFT;
/* Up there we setup a static scancode->keysym 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) { static void QZ_DoKey (int state, NSEvent *event) {
NSString *chars; NSString *chars;
int i; int i;
SDL_keysym key; SDL_keysym key;
/* An event can contain multiple characters */ /* An event can contain multiple characters */
/* I'll ignore this fact for now, since there is only one virtual key code per event */ /* I'll ignore this fact for now, since there is only one virtual key code per event */
chars = [ event characters ]; chars = [ event characters ];
for (i =0; i < 1 /*[ chars length ] */; i++) { for (i =0; i < 1 /*[ chars length ] */; i++) {
key.scancode = [ event keyCode ]; key.scancode = [ event keyCode ];
key.sym = keymap [ key.scancode ]; key.sym = keymap [ key.scancode ];
key.unicode = [ chars characterAtIndex:i]; key.unicode = [ chars characterAtIndex:i];
key.mod = KMOD_NONE; key.mod = KMOD_NONE;
SDL_PrivateKeyboard (state, &key); SDL_PrivateKeyboard (state, &key);
} }
} }
static void QZ_DoModifiers (unsigned int newMods) { static void QZ_DoModifiers (unsigned int newMods) {
const int offset = 18; const int mapping[] = { SDLK_CAPSLOCK, SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA } ;
const int mapping[] = { SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, 0, SDLK_LMETA } ;
int i;
int bit;
SDL_keysym key;
int bit; key.scancode = 0;
SDL_keysym key; key.sym = SDLK_UNKNOWN;
key.scancode = 0; key.unicode = 0;
key.sym = SDLK_UNKNOWN; key.mod = KMOD_NONE;
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) {
/* Iterate through the bits, testing each against the current modifiers */
for (bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1) { unsigned int currentMask, newMask;
unsigned int currentMask, newMask; currentMask = currentMods & bit;
newMask = newMods & bit;
currentMask = currentMods & bit;
newMask = newMods & bit; if ( currentMask &&
currentMask != newMask ) { /* modifier up event */
if ( currentMask &&
currentMask != newMask ) { /* modifier up event */
key.sym = mapping[ currentMask >> offset ]; key.sym = mapping[i];
SDL_PrivateKeyboard (SDL_RELEASED, &key); /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
} if (bit == NSAlphaShiftKeyMask)
else SDL_PrivateKeyboard (SDL_PRESSED, &key);
if ( newMask && SDL_PrivateKeyboard (SDL_RELEASED, &key);
currentMask != newMask ) { /* modifier down event */ }
else
key.sym = mapping [ newMask >> offset ]; if ( newMask &&
SDL_PrivateKeyboard (SDL_PRESSED, &key); currentMask != newMask ) { /* modifier down event */
}
} key.sym = mapping[i];
SDL_PrivateKeyboard (SDL_PRESSED, &key);
currentMods = newMods; /* 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) static void QZ_DoActivate (_THIS)
{ {
inForeground = YES; inForeground = YES;
/* Regrab the mouse */ /* Regrab the mouse */
if (currentGrabMode == SDL_GRAB_ON) { if (currentGrabMode == SDL_GRAB_ON) {
QZ_WarpWMCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2); QZ_WarpWMCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2);
CGAssociateMouseAndMouseCursorPosition (0); CGAssociateMouseAndMouseCursorPosition (0);
} }
/* Hide the mouse cursor if inside the app window */ /* Hide the mouse cursor if inside the app window */
// FIXME if (!QZ_cursor_visible) {
if (!QZ_cursor_visible) { HideCursor ();
HideCursor (); }
}
SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS);
SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS);
} }
static void QZ_DoDeactivate (_THIS) { static void QZ_DoDeactivate (_THIS) {
inForeground = NO; inForeground = NO;
/* Ungrab mouse if it is grabbed */ /* Ungrab mouse if it is grabbed */
if (currentGrabMode == SDL_GRAB_ON) { if (currentGrabMode == SDL_GRAB_ON) {
CGAssociateMouseAndMouseCursorPosition (1); CGAssociateMouseAndMouseCursorPosition (1);
} }
/* Show the mouse cursor */ /* Show the mouse cursor */
// FIXME if (!QZ_cursor_visible) {
if (!QZ_cursor_visible) { ShowCursor ();
ShowCursor (); }
}
SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS);
SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS);
} }
static void QZ_PumpEvents (_THIS) static void QZ_PumpEvents (_THIS)
{ {
NSDate *distantPast; NSDate *distantPast;
NSEvent *event; NSEvent *event;
NSRect winRect; NSRect winRect;
NSRect titleBarRect; NSRect titleBarRect;
NSAutoreleasePool *pool; NSAutoreleasePool *pool;
distantPast = [ [ NSDate distantPast ] retain ]; pool = [ [ NSAutoreleasePool alloc ] init ];
distantPast = [ NSDate distantPast ];
pool = [ [ NSAutoreleasePool alloc ] init ];
winRect = NSMakeRect (0, 0, SDL_VideoSurface->w + 1, SDL_VideoSurface->h + 1);
winRect = NSMakeRect (0, 0, SDL_VideoSurface->w + 1, SDL_VideoSurface->h + 1); titleBarRect = NSMakeRect ( 0, SDL_VideoSurface->h, SDL_VideoSurface->w,
titleBarRect = NSMakeRect ( 0, SDL_VideoSurface->h, SDL_VideoSurface->w, SDL_VideoSurface->h + 22 );
SDL_VideoSurface->h + 22 );
do {
do {
/* Poll for an event. This will not block */
/* Poll for an event. This will not block */ event = [ NSApp nextEventMatchingMask:NSAnyEventMask
event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
untilDate:distantPast inMode: NSDefaultRunLoopMode dequeue:YES ];
inMode: NSDefaultRunLoopMode dequeue:YES ];
if (event != nil) {
if (event != nil) { unsigned int type;
unsigned int type; BOOL isForGameWin;
BOOL isForGameWin;
#define DO_MOUSE_DOWN(button, sendToWindow) do { \ #define DO_MOUSE_DOWN(button, sendToWindow) do { \
if ( inForeground ) { \ if ( inForeground ) { \
if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \ if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \
NSPointInRect([event locationInWindow], winRect) ) \ NSPointInRect([event locationInWindow], winRect) ) \
SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); \ SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); \
} \ } \
else { \ else { \
QZ_DoActivate (this); \ QZ_DoActivate (this); \
} \ } \
[ NSApp sendEvent:event ]; \ [ NSApp sendEvent:event ]; \
} while(0) } while(0)
#define DO_MOUSE_UP(button, sendToWindow) do { \ #define DO_MOUSE_UP(button, sendToWindow) do { \
if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \ if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \
!NSPointInRect([event locationInWindow], titleBarRect) )\ !NSPointInRect([event locationInWindow], titleBarRect) )\
SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0); \ SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0); \
[ NSApp sendEvent:event ]; \ [ NSApp sendEvent:event ]; \
} while(0) } while(0)
type = [ event type ]; type = [ event type ];
isForGameWin = (qz_window == [ event window ]); isForGameWin = (qz_window == [ event window ]);
switch (type) { switch (type) {
case NSLeftMouseDown: case NSLeftMouseDown:
if ( NSCommandKeyMask & currentMods ) { if ( NSCommandKeyMask & currentMods ) {
last_virtual_button = 3; last_virtual_button = 3;
DO_MOUSE_DOWN (3, 0); DO_MOUSE_DOWN (3, 0);
} }
else if ( NSAlternateKeyMask & currentMods ) { else if ( NSAlternateKeyMask & currentMods ) {
last_virtual_button = 2; last_virtual_button = 2;
DO_MOUSE_DOWN (2, 0); DO_MOUSE_DOWN (2, 0);
} }
else { else {
DO_MOUSE_DOWN (1, 1); DO_MOUSE_DOWN (1, 1);
} }
break; break;
case 25: DO_MOUSE_DOWN (2, 0); break; case 25: DO_MOUSE_DOWN (2, 0); break;
case NSRightMouseDown: DO_MOUSE_DOWN (3, 0); break; case NSRightMouseDown: DO_MOUSE_DOWN (3, 0); break;
case NSLeftMouseUp: case NSLeftMouseUp:
if ( last_virtual_button != 0 ) { if ( last_virtual_button != 0 ) {
DO_MOUSE_UP (last_virtual_button, 0); DO_MOUSE_UP (last_virtual_button, 0);
last_virtual_button = 0; last_virtual_button = 0;
} }
else { else {
DO_MOUSE_UP (1, 1); DO_MOUSE_UP (1, 1);
} }
break; break;
case 26: DO_MOUSE_UP (2, 0); break; case 26: DO_MOUSE_UP (2, 0); break;
case NSRightMouseUp: DO_MOUSE_UP (3, 0); break; case NSRightMouseUp: DO_MOUSE_UP (3, 0); break;
case NSSystemDefined: case NSSystemDefined:
//if ([event subtype] == 7) { //if ([event subtype] == 7) {
// unsigned int buttons; // up to 32 mouse button states! // unsigned int buttons; // up to 32 mouse button states!
// buttons = [ event data2 ]; // buttons = [ event data2 ];
//} //}
break; break;
case NSLeftMouseDragged: case NSLeftMouseDragged:
case NSRightMouseDragged: case NSRightMouseDragged:
case 27: case 27:
case NSMouseMoved: case NSMouseMoved:
if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN)
|| NSPointInRect([event locationInWindow], winRect) ) || NSPointInRect([event locationInWindow], winRect) )
{ {
static int moves = 0; static int moves = 0;
NSPoint p; NSPoint p;
if ( SDL_VideoSurface->flags & SDL_FULLSCREEN ) { if ( SDL_VideoSurface->flags & SDL_FULLSCREEN ) {
p = [ NSEvent mouseLocation ]; p = [ NSEvent mouseLocation ];
p.y = [[NSScreen mainScreen] frame].size.height - p.y; p.y = [[NSScreen mainScreen] frame].size.height - p.y;
} else { } else {
p = [ event locationInWindow ]; p = [ event locationInWindow ];
p.y = SDL_VideoSurface->h - p.y; p.y = SDL_VideoSurface->h - p.y;
} }
if ( (moves % 10) == 0 ) { if ( (moves % 10) == 0 ) {
SDL_PrivateMouseMotion (0, 0, p.x, p.y); SDL_PrivateMouseMotion (0, 0, p.x, p.y);
} }
else { else {
CGMouseDelta dx, dy; CGMouseDelta dx, dy;
CGGetLastMouseDelta (&dx, &dy); CGGetLastMouseDelta (&dx, &dy);
SDL_PrivateMouseMotion (0, 1, dx, dy); SDL_PrivateMouseMotion (0, 1, dx, dy);
} }
moves++; moves++;
} }
break; break;
case NSScrollWheel: case NSScrollWheel:
{ {
if (NSPointInRect([ event locationInWindow ], winRect)) { if (NSPointInRect([ event locationInWindow ], winRect)) {
float dy; float dy;
dy = [ event deltaY ]; dy = [ event deltaY ];
if ( dy > 0.0 ) /* Scroll up */ if ( dy > 0.0 ) /* Scroll up */
SDL_PrivateMouseButton (SDL_PRESSED, 4, 0, 0); SDL_PrivateMouseButton (SDL_PRESSED, 4, 0, 0);
else /* Scroll down */ else /* Scroll down */
SDL_PrivateMouseButton (SDL_PRESSED, 5, 0, 0); SDL_PrivateMouseButton (SDL_PRESSED, 5, 0, 0);
} }
} }
break; break;
case NSKeyUp: case NSKeyUp:
QZ_DoKey (SDL_RELEASED, event); QZ_DoKey (SDL_RELEASED, event);
break; break;
case NSKeyDown: case NSKeyDown:
QZ_DoKey (SDL_PRESSED, event); QZ_DoKey (SDL_PRESSED, event);
break; break;
case NSFlagsChanged: case NSFlagsChanged:
QZ_DoModifiers( [ event modifierFlags ] ); QZ_DoModifiers( [ event modifierFlags ] );
break; break;
// case NSMouseEntered: break; /* case NSMouseEntered: break; */
// case NSMouseExited: break; /* case NSMouseExited: break; */
case NSAppKitDefined: case NSAppKitDefined:
switch ( [ event subtype ] ) { switch ( [ event subtype ] ) {
case NSApplicationActivatedEventType: case NSApplicationActivatedEventType:
QZ_DoActivate (this); QZ_DoActivate (this);
break; break;
case NSApplicationDeactivatedEventType: case NSApplicationDeactivatedEventType:
QZ_DoDeactivate (this); QZ_DoDeactivate (this);
break; break;
} }
[ NSApp sendEvent:event ]; [ NSApp sendEvent:event ];
break; break;
// case NSApplicationDefined: break; /* case NSApplicationDefined: break; */
// case NSPeriodic: break; /* case NSPeriodic: break; */
// case NSCursorUpdate: break; /* case NSCursorUpdate: break; */
default: default:
[ NSApp sendEvent:event ]; [ NSApp sendEvent:event ];
} }
} }
} while (event != nil); } while (event != nil);
[ pool release ]; [ pool release ];
[ distantPast release ];
} }
...@@ -25,8 +25,6 @@ ...@@ -25,8 +25,6 @@
/* Some variables to share among files, put in device structure eventually */ /* Some variables to share among files, put in device structure eventually */
static SDL_GrabMode currentGrabMode = SDL_GRAB_OFF; static SDL_GrabMode currentGrabMode = SDL_GRAB_OFF;
static BOOL inForeground = YES; 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 */ static char QZ_Error[255]; /* Global error buffer to temporarily store more informative error messages */
/* Include files into one compile unit...break apart eventually */ /* Include files into one compile unit...break apart eventually */
......
...@@ -32,6 +32,7 @@ AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run ...@@ -32,6 +32,7 @@ AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run
fi fi
fi fi
AC_REQUIRE([AC_CANONICAL_TARGET])
AC_PATH_PROG(SDL_CONFIG, sdl-config, no) AC_PATH_PROG(SDL_CONFIG, sdl-config, no)
min_sdl_version=ifelse([$1], ,0.11.0,$1) min_sdl_version=ifelse([$1], ,0.11.0,$1)
AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) AC_MSG_CHECKING(for SDL - version >= $min_sdl_version)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment