st/code/macosx/macosx_input.m

962 lines
31 KiB
Objective-C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#include <ApplicationServices/ApplicationServices.h>
#include <pthread.h>
#import "../client/client.h"
#import "macosx_local.h"
#import "../renderer/tr_local.h"
#import "Q3Controller.h"
//#import "CGMouseDeltaFix.h"
#import "macosx_timers.h"
#import "macosx_display.h" // For Sys_SetScreenFade
#import <drivers/event_status_driver.h>
#import <sys/types.h>
#import <sys/time.h>
#import <unistd.h>
static qboolean inputActive;
static NSDate *distantPast;
static cvar_t *in_nomouse;
static cvar_t *in_showevents;
static cvar_t *in_mouseLowEndSlope;
static cvar_t *in_mouseHighEndCutoff;
static cvar_t *in_disableOSMouseScaling;
static void Sys_StartMouseInput();
static void Sys_StopMouseInput();
static void Sys_StartKeyboardInput();
static void Sys_StopKeyboardInput();
static qboolean mouseactive = qfalse;
static qboolean first_3d_mouse_event = qfalse;
static BOOL inputRectValid = NO;
static CGRect inputRect;
static NXMouseScaling originalScaling;
static unsigned int currentModifierFlags;
pthread_mutex_t queue_mutex;
static void Sys_PreventMouseMovement(CGPoint point)
{
CGEventErr err;
if (point.x == 0.0f && point.y == 0.0f) return;
// Put the mouse in the position we want to leave it at
err = CGWarpMouseCursorPosition(point);
if (err != CGEventNoErr) {
Sys_Error("Could not disable mouse movement, CGWarpMouseCursorPosition returned %d\n", err);
}
//Com_Printf("**** Calling CGAssociateMouseAndMouseCursorPosition(false)\n");
err = CGAssociateMouseAndMouseCursorPosition(false);
if (err != CGEventNoErr) {
Sys_Error("Could not disable mouse movement, CGAssociateMouseAndMouseCursorPosition returned %d\n", err);
}
}
static void Sys_ReenableMouseMovement()
{
CGEventErr err;
//Com_Printf("**** Calling CGAssociateMouseAndMouseCursorPosition(true)\n");
err = CGAssociateMouseAndMouseCursorPosition(true);
if (err != CGEventNoErr) {
Sys_Error("Could not reenable mouse movement, CGAssociateMouseAndMouseCursorPosition returned %d\n", err);
}
// Leave the mouse where it was -- don't warp here.
}
void Sys_InitInput(void)
{
// no input with dedicated servers
if ( com_dedicated->integer ) {
return;
}
// The Cvars don't seem to work really early.
//[(Q3Controller *)[NSApp delegate] showBanner];
Com_Printf( "------- Input Initialization -------\n" );
pthread_mutex_init(&queue_mutex, NULL);
if (!distantPast)
distantPast = [[NSDate distantPast] retain];
// For hide support. If we don't do this, then the command key will get stuck on when we hide (since we won't get the flags changed event when it goes up).
currentModifierFlags = 0;
in_nomouse = Cvar_Get( "in_nomouse", "0", 0 );
in_showevents = Cvar_Get( "in_showevents", "0", 0 );
// these defaults were arrived at via emprical testing between a Windows box and a Mac OS X box
#define ACT_LIKE_WINDOWS
#ifdef ACT_LIKE_WINDOWS
in_mouseLowEndSlope = Cvar_Get("in_mouseLowEndSlope", "3.5", CVAR_ARCHIVE);
if (in_mouseLowEndSlope->value < 1) {
Cvar_Set("in_mouseLowEndSlope", "1");
}
#else
in_mouseLowEndSlope = Cvar_Get("in_mouseLowEndSlope", "1", CVAR_ARCHIVE);
if (in_mouseLowEndSlope->value < 1) {
Cvar_Set("in_mouseLowEndSlope", "1");
}
#endif
in_mouseHighEndCutoff = Cvar_Get("in_mouseHighEndCutoff", "20", CVAR_ARCHIVE);
if (in_mouseLowEndSlope->value < 1) {
Cvar_Set("in_mouseHighEndCutoff", "1");
}
in_disableOSMouseScaling = Cvar_Get("in_disableOSMouseScaling", "1", CVAR_ARCHIVE );
inputActive = qtrue;
Sys_StartKeyboardInput();
// if ( in_nomouse->integer == 0 )
// Sys_StartMouseInput();
// else
// Com_Printf( " in_nomouse is set, skipping.\n" );
Com_Printf( "------------------------------------\n" );
}
void Sys_ShutdownInput(void)
{
// no input with dedicated servers
if ( !com_dedicated || com_dedicated->integer ) {
return;
}
Com_Printf( "------- Input Shutdown -------\n" );
if ( !inputActive ) {
return;
}
pthread_mutex_destroy(&queue_mutex);
inputActive = qfalse;
Sys_StopKeyboardInput();
if (mouseactive)
Sys_StopMouseInput();
Com_Printf( "------------------------------\n" );
}
static void Sys_LockMouseInInputRect(CGRect rect)
{
CGPoint center;
center.x = rect.origin.x + rect.size.width / 2.0;
center.y = rect.origin.y + rect.size.height / 2.0;
// Now, put the mouse in the middle of the input rect (anywhere over it would do)
// and don't allow it to move. This means that the user won't be able to accidentally
// select another application.
Sys_PreventMouseMovement(center);
}
extern void Sys_UpdateWindowMouseInputRect(void);
static void Sys_StartMouseInput()
{
NXEventHandle eventStatus;
CGMouseDelta dx, dy;
if (mouseactive) {
//Com_Printf("**** Attempted to start mouse input while already started\n");
return;
}
Com_Printf("Starting mouse input\n");
mouseactive = qtrue;
if (inputRectValid && !glConfig.isFullscreen)
// Make sure that if window moved we don't hose the user...
Sys_UpdateWindowMouseInputRect();
Sys_LockMouseInInputRect(inputRect);
// Grab any mouse delta information to reset the last delta buffer
CGGetLastMouseDelta(&dx, &dy);
first_3d_mouse_event = qtrue;
// Turn off mouse scaling
if (in_disableOSMouseScaling->integer==0 && (eventStatus = NXOpenEventStatus())) {
NXMouseScaling newScaling;
NXGetMouseScaling(eventStatus, &originalScaling);
newScaling.numScaleLevels = 1;
newScaling.scaleThresholds[0] = 1;
newScaling.scaleFactors[0] = -1;
NXSetMouseScaling(eventStatus, &newScaling);
NXCloseEventStatus(eventStatus);
}
[NSCursor hide];
}
static void Sys_StopMouseInput()
{
NXEventHandle eventStatus;
if (!mouseactive) {
//Com_Printf("**** Attempted to stop mouse input while already stopped\n");
return;
}
Com_Printf("Stopping mouse input\n");
// Restore mouse scaling
if (in_disableOSMouseScaling->integer == 0 && (eventStatus = NXOpenEventStatus())) {
NXSetMouseScaling(eventStatus, &originalScaling);
NXCloseEventStatus(eventStatus);
}
mouseactive = qfalse;
Sys_ReenableMouseMovement();
[NSCursor unhide];
}
void Sys_SetCursor( int c ) {
}
static void Sys_StartKeyboardInput()
{
#ifdef USE_PLUGIN
NXEventHandle eventStatus;
eventStatus = NXOpenEventStatus();
//Save key repeat settings
oldRepeatInterval = NXKeyRepeatInterval(eventStatus);
oldRepeatThreshold = NXKeyRepeatThreshold(eventStatus);
// No repeat events for a long time
NXSetKeyRepeatInterval(eventStatus, 3456000.0f);
NXSetKeyRepeatThreshold(eventStatus, 3456000.0f);
NXCloseEventStatus(eventStatus);
#endif
}
static void Sys_StopKeyboardInput()
{
#ifdef USE_PLUGIN
NXEventHandle eventStatus;
eventStatus = NXOpenEventStatus();
// No repeat events for a long time
NXSetKeyRepeatInterval(eventStatus, oldRepeatInterval);
NXSetKeyRepeatThreshold(eventStatus, oldRepeatThreshold);
NXCloseEventStatus(eventStatus);
#endif
}
//===========================================================================
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
static char *Sys_ConsoleInput(void)
{
extern qboolean stdin_active;
static char text[256];
int len;
fd_set fdset;
struct timeval timeout;
if (!com_dedicated || !com_dedicated->integer)
return NULL;
if (!stdin_active)
return NULL;
FD_ZERO(&fdset);
FD_SET(fileno(stdin), &fdset);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(fileno(stdin), &fdset))
return NULL;
len = read (fileno(stdin), text, sizeof(text));
if (len == 0) { // eof!
stdin_active = qfalse;
return NULL;
}
if (len < 1)
return NULL;
text[len-1] = 0; // rip off the /n and terminate
return text;
}
//===========================================================================
// Mouse input
//===========================================================================
void Sys_SetMouseInputRect(CGRect newRect)
{
inputRectValid = YES;
inputRect = newRect;
Com_Printf("**** inputRect = (%f, %f, %f, %f)\n", newRect.origin.x, newRect.origin.y, newRect.size.width, newRect.size.height);
if (mouseactive)
Sys_LockMouseInInputRect(inputRect);
}
static void Sys_ProcessMouseMovedEvent(NSEvent *mouseMovedEvent, int currentTime)
{
if (!cls.keyCatchers) {
Sys_QueEvent(currentTime, SE_MOUSE_REL, (int)[mouseMovedEvent deltaX], (int)[mouseMovedEvent deltaY], 0, NULL );
return;
}
NSPoint eventLocation;
NSPoint local_point;
eventLocation = [mouseMovedEvent locationInWindow];
if ( g_wv.plugin ) {
local_point = [g_wv.plugin convertPoint:eventLocation fromView:nil];
local_point.y = (glConfig.vidHeight - local_point.y);
} else if ( glw_state.window ) {
local_point = [[glw_state.window contentView] convertPoint:eventLocation fromView:nil];
local_point.y = (glConfig.vidHeight - local_point.y);
} else {
NSPoint p;
NSRect f;
local_point = eventLocation;
f = [[NSScreen mainScreen] frame];
p.x = local_point.x - glw_state.x;
p.y = f.size.height - local_point.y;
Sys_QueEvent(currentTime, SE_MOUSE_ABS, (int)p.x, (int)p.y, 0, NULL);
return;
}
Sys_QueEvent(currentTime, SE_MOUSE_ABS, (int)local_point.x, (int)local_point.y, 0, NULL);
}
// If we are 'paused' (i.e., in any state that our normal key bindings aren't in effect), then interpret cmd-h and cmd-tab as hiding the application.
static qboolean maybeHide()
{
if ((currentModifierFlags & NSCommandKeyMask) == 0)
return qfalse;
return Sys_Hide();
}
static inline void sendEventForCharacter(NSEvent *event, unichar character, qboolean keyDownFlag, int currentTime)
{
if (in_showevents->integer)
Com_Printf("CHARACTER: 0x%02x down=%d\n", character, keyDownFlag);
#ifdef OMNI_TIMER
if (character == NSF9FunctionKey && !keyDownFlag) {
// Log and reset the root timer. We should currently only have the root on the stack.
OTStackPopRoot();
OTStackReportResults(NULL);
OTStackReset();
OTStackPushRoot(rootNode);
}
#endif
switch (character) {
case 0x03:
Sys_QueEvent(currentTime, SE_KEY, K_KP_ENTER, keyDownFlag, 0, NULL);
break;
case '\b':
case '\177':
Sys_QueEvent(currentTime, SE_KEY, K_BACKSPACE, keyDownFlag, 0, NULL);
if (keyDownFlag) {
Sys_QueEvent(currentTime, SE_CHAR, '\b', 0, 0, NULL);
}
break;
case '\t':
if (maybeHide())
return;
Sys_QueEvent(currentTime, SE_KEY, K_TAB, keyDownFlag, 0, NULL);
if (keyDownFlag) {
Sys_QueEvent(currentTime, SE_CHAR, '\t', 0, 0, NULL);
}
break;
case '\r':
case '\n':
Sys_QueEvent(currentTime, SE_KEY, K_ENTER, keyDownFlag, 0, NULL);
if (keyDownFlag) {
Sys_QueEvent(currentTime, SE_CHAR, '\r', 0, 0, NULL);
}
break;
case '\033':
Sys_QueEvent(currentTime, SE_KEY, K_ESCAPE, keyDownFlag, 0, NULL);
break;
case ' ':
Sys_QueEvent(currentTime, SE_KEY, K_SPACE, keyDownFlag, 0, NULL);
if (keyDownFlag) {
Sys_QueEvent(currentTime, SE_CHAR, ' ', 0, 0, NULL);
}
break;
case NSUpArrowFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_UPARROW, keyDownFlag, 0, NULL);
break;
case NSDownArrowFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_DOWNARROW, keyDownFlag, 0, NULL);
break;
case NSLeftArrowFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_LEFTARROW, keyDownFlag, 0, NULL);
break;
case NSRightArrowFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_RIGHTARROW, keyDownFlag, 0, NULL);
break;
case NSF1FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F1, keyDownFlag, 0, NULL);
break;
case NSF2FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F2, keyDownFlag, 0, NULL);
break;
case NSF3FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F3, keyDownFlag, 0, NULL);
break;
case NSF4FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F4, keyDownFlag, 0, NULL);
break;
case NSF5FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F5, keyDownFlag, 0, NULL);
break;
case NSF6FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F6, keyDownFlag, 0, NULL);
break;
case NSF7FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F7, keyDownFlag, 0, NULL);
break;
case NSF8FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F8, keyDownFlag, 0, NULL);
break;
case NSF9FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F9, keyDownFlag, 0, NULL);
break;
case NSF10FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F10, keyDownFlag, 0, NULL);
break;
case NSF11FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F11, keyDownFlag, 0, NULL);
break;
case NSF12FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_F12, keyDownFlag, 0, NULL);
break;
case NSF13FunctionKey:
Sys_QueEvent(currentTime, SE_KEY, '`', keyDownFlag, 0, NULL);
if (keyDownFlag) {
Sys_QueEvent(currentTime, SE_CHAR, '`', 0, 0, NULL);
}
break;
case NSInsertFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_INS, keyDownFlag, 0, NULL);
break;
case NSDeleteFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_DEL, keyDownFlag, 0, NULL);
break;
case NSPageDownFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_PGDN, keyDownFlag, 0, NULL);
break;
case NSPageUpFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_PGUP, keyDownFlag, 0, NULL);
break;
case NSHomeFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_HOME, keyDownFlag, 0, NULL);
break;
case NSEndFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_END, keyDownFlag, 0, NULL);
break;
case NSPauseFunctionKey:
Sys_QueEvent(currentTime, SE_KEY, K_PAUSE, keyDownFlag, 0, NULL);
break;
default:
if ([event modifierFlags] & NSNumericPadKeyMask) {
switch (character) {
case '0':
Sys_QueEvent(currentTime, SE_KEY, K_KP_INS, keyDownFlag, 0, NULL);
break;
case '1':
Sys_QueEvent(currentTime, SE_KEY, K_KP_END, keyDownFlag, 0, NULL);
break;
case '2':
Sys_QueEvent(currentTime, SE_KEY, K_KP_DOWNARROW, keyDownFlag, 0, NULL);
break;
case '3':
Sys_QueEvent(currentTime, SE_KEY, K_KP_PGDN, keyDownFlag, 0, NULL);
break;
case '4':
Sys_QueEvent(currentTime, SE_KEY, K_KP_LEFTARROW, keyDownFlag, 0, NULL);
break;
case '5':
Sys_QueEvent(currentTime, SE_KEY, K_KP_5, keyDownFlag, 0, NULL);
break;
case '6':
Sys_QueEvent(currentTime, SE_KEY, K_KP_RIGHTARROW, keyDownFlag, 0, NULL);
break;
case '7':
Sys_QueEvent(currentTime, SE_KEY, K_KP_HOME, keyDownFlag, 0, NULL);
break;
case '8':
Sys_QueEvent(currentTime, SE_KEY, K_KP_UPARROW, keyDownFlag, 0, NULL);
break;
case '9':
Sys_QueEvent(currentTime, SE_KEY, K_KP_PGUP, keyDownFlag, 0, NULL);
break;
case '.':
case ',':
Sys_QueEvent(currentTime, SE_KEY, K_KP_DEL, keyDownFlag, 0, NULL);
break;
case '+':
Sys_QueEvent(currentTime, SE_KEY, K_KP_PLUS, keyDownFlag, 0, NULL);
break;
case '-':
Sys_QueEvent(currentTime, SE_KEY, K_KP_MINUS, keyDownFlag, 0, NULL);
break;
case '*':
Sys_QueEvent(currentTime, SE_KEY, K_KP_STAR, keyDownFlag, 0, NULL);
break;
case '/':
Sys_QueEvent(currentTime, SE_KEY, K_KP_SLASH, keyDownFlag, 0, NULL);
break;
case '=':
Sys_QueEvent(currentTime, SE_KEY, K_KP_EQUALS, keyDownFlag, 0, NULL);
break;
default:
//NSLog(@"TODO: Implement character %d", (int)character);
break;
}
} else if (character >= 'a' && character <= 'z') {
if (character == 'h') {
if (maybeHide())
return;
}
Sys_QueEvent(currentTime, SE_KEY, character, keyDownFlag, 0, NULL);
if (keyDownFlag) {
Sys_QueEvent(currentTime, SE_CHAR, (char)character, 0, 0, NULL);
}
} else if (character >= 'A' && character <= 'Z') {
Sys_QueEvent(currentTime, SE_KEY, 'a' + (character - 'A'), keyDownFlag, 0, NULL);
if (keyDownFlag) {
Sys_QueEvent(currentTime, SE_CHAR, character, 0, 0, NULL);
}
} else if (character >= 32 && character < 127) {
Sys_QueEvent(currentTime, SE_KEY, character, keyDownFlag, 0, NULL);
if (keyDownFlag) {
Sys_QueEvent(currentTime, SE_CHAR, (char)character, 0, 0, NULL);
}
} else {
//NSLog(@"TODO: Implement character %d", (int)character);
}
break;
}
}
static inline void processKeyEvent(NSEvent *keyEvent, qboolean keyDownFlag, int currentTime)
{
NSEventType eventType;
NSString *characters;
unsigned int characterIndex, characterCount;
eventType = [keyEvent type];
characters = [keyEvent charactersIgnoringModifiers];
characterCount = [characters length];
for (characterIndex = 0; characterIndex < characterCount; characterIndex++) {
sendEventForCharacter(keyEvent, [characters characterAtIndex:characterIndex], keyDownFlag, currentTime);
}
}
static inline void sendEventForMaskChangeInFlags(int quakeKey, unsigned int modifierMask, unsigned int newModifierFlags, int currentTime)
{
BOOL oldHadModifier, newHasModifier;
oldHadModifier = (currentModifierFlags & modifierMask) != 0;
newHasModifier = (newModifierFlags & modifierMask) != 0;
if (oldHadModifier != newHasModifier) {
// NSLog(@"Key %d posted for modifier mask modifierMask", quakeKey);
Sys_QueEvent(currentTime, SE_KEY, quakeKey, newHasModifier, 0, NULL);
}
}
static inline void processFlagsChangedEvent(NSEvent *flagsChangedEvent, int currentTime)
{
int newModifierFlags;
newModifierFlags = [flagsChangedEvent modifierFlags];
sendEventForMaskChangeInFlags(K_COMMAND, NSCommandKeyMask, newModifierFlags, currentTime);
sendEventForMaskChangeInFlags(K_CAPSLOCK, NSAlphaShiftKeyMask, newModifierFlags, currentTime);
sendEventForMaskChangeInFlags(K_ALT, NSAlternateKeyMask, newModifierFlags, currentTime);
sendEventForMaskChangeInFlags(K_CTRL, NSControlKeyMask, newModifierFlags, currentTime);
sendEventForMaskChangeInFlags(K_SHIFT, NSShiftKeyMask, newModifierFlags, currentTime);
currentModifierFlags = newModifierFlags;
}
static inline void processSystemDefinedEvent(NSEvent *systemDefinedEvent, int currentTime)
{
static int oldButtons = 0;
int buttonsDelta;
int buttons;
int isDown;
if ([systemDefinedEvent subtype] == 7) {
// if (!cls.keyCatchers)
// return;
buttons = [systemDefinedEvent data2];
buttonsDelta = oldButtons ^ buttons;
//Com_Printf("uberbuttons: %08lx %08lx\n",buttonsDelta,buttons);
if (buttonsDelta & 1) {
isDown = buttons & 1;
Sys_QueEvent(currentTime, SE_KEY, K_MOUSE1, isDown, 0, NULL);
if (in_showevents->integer) {
Com_Printf("MOUSE2: %s\n", isDown ? "down" : "up");
}
}
if (buttonsDelta & 2) {
isDown = buttons & 2;
Sys_QueEvent(currentTime, SE_KEY, K_MOUSE2, isDown, 0, NULL);
if (in_showevents->integer) {
Com_Printf("MOUSE3: %s\n", isDown ? "down" : "up");
}
}
if (buttonsDelta & 4) {
isDown = buttons & 4;
Sys_QueEvent(currentTime, SE_KEY, K_MOUSE3, isDown, 0, NULL);
if (in_showevents->integer) {
Com_Printf("MOUSE1: %s\n", isDown ? "down" : "up");
}
}
if (buttonsDelta & 8) {
isDown = buttons & 8;
Sys_QueEvent(currentTime, SE_KEY, K_MOUSE4, isDown, 0, NULL);
if (in_showevents->integer) {
Com_Printf("MOUSE4: %s\n", isDown ? "down" : "up");
}
}
if (buttonsDelta & 16) {
isDown = buttons & 16;
Sys_QueEvent(currentTime, SE_KEY, K_MOUSE5, isDown, 0, NULL);
if (in_showevents->integer) {
Com_Printf("MOUSE5: %s\n", isDown ? "down" : "up");
}
}
oldButtons = buttons;
}
}
inline void processEvent(NSEvent *event, int currentTime)
{
NSEventType eventType;
if (!inputActive)
return;
eventType = [event type];
if (in_showevents->integer)
NSLog(@"event = %@", event);
switch (eventType) {
// These six event types are ignored since we do all of our mouse down/up process via the uber-mouse system defined event. We have to accept these events however since they get enqueued and the queue will fill up if we don't.
case NSLeftMouseDown:
[NSApp sendEvent:event];
//Sys_QueEvent(currentTime, SE_KEY, K_MOUSE1, qtrue, 0, NULL);
return;
case NSLeftMouseUp:
[NSApp sendEvent:event];
//Sys_QueEvent(currentTime, SE_KEY, K_MOUSE1, qfalse, 0, NULL);
return;
case NSRightMouseDown:
[NSApp sendEvent:event];
//Sys_QueEvent(currentTime, SE_KEY, K_MOUSE2, qtrue, 0, NULL);
return;
case NSRightMouseUp:
[NSApp sendEvent:event];
//Sys_QueEvent(currentTime, SE_KEY, K_MOUSE2, qfalse, 0, NULL);
return;
case 25: // other mouse down
[NSApp sendEvent:event];
return;
case 26: // other mouse up
[NSApp sendEvent:event];
return;
case NSMouseMoved:
case NSLeftMouseDragged:
case NSRightMouseDragged:
case 27: // other mouse dragged
if (first_3d_mouse_event == qtrue) {
first_3d_mouse_event = qfalse;
return;
}
Sys_ProcessMouseMovedEvent(event, currentTime);
return;
case NSKeyDown:
case NSKeyUp:
processKeyEvent(event, eventType == NSKeyDown, currentTime);
return;
case NSFlagsChanged:
processFlagsChangedEvent(event, currentTime);
return;
case NSSystemDefined:
processSystemDefinedEvent(event, currentTime);
return;
case NSScrollWheel:
if ([event deltaY] < 0.0) {
Sys_QueEvent(currentTime, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL );
Sys_QueEvent(currentTime, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL );
} else {
Sys_QueEvent(currentTime, SE_KEY, K_MWHEELUP, qtrue, 0, NULL );
Sys_QueEvent(currentTime, SE_KEY, K_MWHEELUP, qfalse, 0, NULL );
}
return;
default:
break;
}
[NSApp sendEvent:event];
}
static void Sys_SendKeyEvents(int currentTime)
{
#ifndef DEDICATED
NSEvent *event;
NSDate *timeout;
extern float SNDDMA_GetBufferDuration();
timeout = distantPast;
if (Sys_IsHidden)
timeout = [NSDate dateWithTimeIntervalSinceNow: 0.25 * SNDDMA_GetBufferDuration()];
// This gets call regardless of whether inputActive is true or not. This is important since we need to be poking the event queue in order for the unhide event to make its way through the system. This means that when we hide, we can just shut down the input system and reeanbled it when we unhide.
// [NSApp nextEventMatchingMask
while ((event = [NSApp nextEventMatchingMask: NSAnyEventMask
untilDate: timeout
inMode: NSDefaultRunLoopMode
dequeue:YES])) {
if (Sys_IsHidden) {
// Just let NSApp handle events so that we'll get the app activation event
[NSApp sendEvent: event];
timeout = [NSDate dateWithTimeIntervalSinceNow: 0.1];
} else {
static int lastEventTime = 0;
static BOOL lastEventTimeValid = NO;
// Mac OS X 10.0.3 has a bug where the if the monitor goes to sleep in fullscreen GL mode, the gamma won't be restored. We'll restore the gamma if there is a pause while in the game of more than 10 seconds. We don't do this on the 'Sys_IsHidden' branch since unhiding will restore the monitor gamma.
if ((currentTime - lastEventTime > 1 * 1000) && lastEventTimeValid) {
//Com_Printf("Restoring monitor gamma after being idle for %f seconds.\n", (currentTime - lastEventTime) / 1000.0);
//[NSCursor hide];
Sys_SetScreenFade(&glw_state.inGameTable, 1.0);
}
lastEventTime = [event timestamp] * 1000.0; //currentTime;
lastEventTimeValid = YES;
processEvent(event, lastEventTime);
}
}
#endif
}
/*
========================================================================
EVENT LOOP
========================================================================
*/
extern qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message );
#define MAX_QUED_EVENTS 256
#define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 )
static sysEvent_t eventQue[MAX_QUED_EVENTS];
static int eventHead, eventTail;
static byte sys_packetReceived[MAX_MSGLEN];
/*
================
Sys_QueEvent
A time of 0 will get the current time
Ptr should either be null, or point to a block of data that can
be freed by the game later.
================
*/
void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
sysEvent_t *ev;
int i,j;
#ifndef DEDICATED
if (in_showevents->integer)
NSLog(@"EVENT ENQUEUE: time=%d type=%d value=0x%08x value2=0x%08x\n", time, type, value, value2);
#endif
if ( eventHead - eventTail >= MAX_QUED_EVENTS ) {
Com_Printf("Sys_QueEvent: overflow\n");
}
if ( !time ) {
time = Sys_Milliseconds();
}
// insert it by time
for ( i = eventTail ; i < eventHead ; i++ ) {
ev = &eventQue[ i & MASK_QUED_EVENTS ];
if ( ev->evTime > time ) {
break;
}
}
// insert before i
for ( j = eventHead ; j > i ; j-- ) {
eventQue[ j & MASK_QUED_EVENTS ] = eventQue[ (j-1) & MASK_QUED_EVENTS ];
}
ev = &eventQue[ i & MASK_QUED_EVENTS ];
ev->evTime = time;
ev->evType = type;
ev->evValue = value;
ev->evValue2 = value2;
ev->evPtrLength = ptrLength;
ev->evPtr = ptr;
pthread_mutex_lock(&queue_mutex);
eventHead++;
pthread_mutex_unlock(&queue_mutex);
}
/*
================
Sys_GetEvent
================
*/
sysEvent_t Sys_GetEvent( void )
{
sysEvent_t ev;
char *s;
msg_t netmsg;
netadr_t adr;
int currentTime;
// return if we have data
pthread_mutex_lock(&queue_mutex);
if (eventHead > eventTail) {
pthread_mutex_unlock(&queue_mutex);
eventTail++;
return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
}
pthread_mutex_unlock(&queue_mutex);
// The queue must be empty. Check all of the event sources. If the events are
// already in the queue, we can't imply any real ordering, so we'll avoid extra
// system calls and give them all the same time.
currentTime = Sys_Milliseconds();
// Check for mouse and keyboard events
Sys_SendKeyEvents(currentTime);
// check for console commands
s = Sys_ConsoleInput();
if ( s ) {
char *b;
int len;
len = strlen( s ) + 1;
b = Z_Malloc( len );
strcpy( b, s );
Sys_QueEvent( currentTime, SE_CONSOLE, 0, 0, len, b );
}
// During debugging it is sometimes usefull to be able to start/stop mouse input.
// Don't turn on the input when we've disabled it because we're hidden, however.
if (!com_dedicated->integer) {
if (!Sys_IsHidden) {
if ( cls.keyCatchers || (cls.state < CA_ACTIVE) )
Sys_StopMouseInput();
else
Sys_StartMouseInput();
}
}
// check for network packets
MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) );
adr.type = NA_IP;
if ( Sys_GetPacket ( &adr, &netmsg ) ) {
netadr_t *buf;
int len;
// copy out to a seperate buffer for qeueing
len = sizeof( netadr_t ) + netmsg.cursize;
buf = Z_Malloc( len );
*buf = adr;
memcpy( buf+1, netmsg.data, netmsg.cursize );
Sys_QueEvent( currentTime, SE_PACKET, 0, 0, len, buf );
}
// If we got an event, return it
if (eventHead > eventTail) {
eventTail++;
return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
}
// Otherwise, return an empty event to indicate that there are no events pending.
memset( &ev, 0, sizeof( ev ) );
ev.evTime = currentTime;
return ev;
}