src/video/quartz/SDL_QuartzEvents.m
author Ryan C. Gordon <icculus@icculus.org>
Fri, 27 Dec 2002 20:52:41 +0000
changeset 563 04dcaf3da918
parent 561 4bcf7dd06c47
child 615 7ec821f3cbd0
permissions -rw-r--r--
Massive Quartz input enhancements from Darrell Walisser. His email:

Enclosed is a patch that addresses the following:

--Various minor cleanups.
Removed dead/obsolete code, made some style cleanups

--Mouse Events
Now keep track of what button(s) were pressed so we know when to send
the mouse up event. This fixes the case where the mouse is dragged
outside of the game window and released (in which case we want to send
the mouse up event even though the mouse is outside the game window).

--Input Grabbing
Here is my take on the grabbing situation, which is the basis for the
new implementation.

There are 3 grab states, ungrabbed (UG), visible (VG), and invisible
(IG). Both VG and IG keep the mouse constrained to the window and
produce relative motion events. In VG the cursor is visible (duh), in
IG it is not. In VG, absolute motion events also work.

There are 6 actions that can affect grabbing:

1. Set Fullscreen/Window (F/W). In fullscreen, a visible grab should do
nothing. However, a fullscreen visible grab can be treated just like a
windowed visible grab, which is what I have done to help simplify
things.

2. Cursor hide/show (H/S). If the cursor is hidden when grabbing, the
grab is an invisible grab. If the cursor is visible, the grab should
just constrain the mouse to the window.

3. Input grab/ungrab(G/U). If grabbed, the cursor should be confined to
the window as should the keyboard input. On Mac OS X, the keyboard
input is implicitly grabbed by confining the cursor, except for
command-tab which can switch away from the application. Should the
window come to the foreground if the application is deactivated and
grab input is called? This isn't necessary in this implementation
because the grab state will be asserted upon activation.

Using my notation, these are all the cases that need to be handled
(state + action = new state).

UG+U = UG
UG+G = VG or IG, if cursor is visible or not
UG+H = UG
UG+S = UG

VG+U = UG
VG+G = VG
VG+H = IG
VG+S = VG

IG+U = UG
IG+G = IG
IG+H = IG
IG+S = VG

The cases that result in the same state can be ignored in the code,
which cuts it down to just 5 cases.

Another issue is what happens when the app loses/gains input focus from
deactivate/activate or iconify/deiconify. I think that if input focus
is ever lost (outside of SDL's control), the grab state should be
suspended and the cursor should become visible and active again. When
regained, the cursor should reappear in its original location and/or
grab state. This way, when reactivating the cursor is still in the same
position as before so apps shouldn't get confused when the next motion
event comes in. This is what I've done in this patch.
slouken@47
     1
/*
slouken@390
     2
    SDL - Simple DirectMedia Layer
slouken@390
     3
    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002    Sam Lantinga
slouken@47
     4
slouken@390
     5
    This library is free software; you can redistribute it and/or
slouken@390
     6
    modify it under the terms of the GNU Library General Public
slouken@390
     7
    License as published by the Free Software Foundation; either
slouken@390
     8
    version 2 of the License, or (at your option) any later version.
slouken@47
     9
slouken@390
    10
    This library is distributed in the hope that it will be useful,
slouken@390
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@390
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@390
    13
    Library General Public License for more details.
slouken@47
    14
slouken@390
    15
    You should have received a copy of the GNU Library General Public
slouken@390
    16
    License along with this library; if not, write to the Free
slouken@390
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@47
    18
slouken@390
    19
    Sam Lantinga
slouken@390
    20
    slouken@libsdl.org
slouken@47
    21
*/
slouken@511
    22
#include <stdlib.h>	// For getenv()
slouken@555
    23
#include <IOKit/IOMessage.h> // For wake from sleep detection
slouken@555
    24
#include <IOKit/pwr_mgt/IOPMLib.h> // For wake from sleep detection
slouken@47
    25
#include "SDL_QuartzKeys.h"
slouken@47
    26
slouken@390
    27
static void     QZ_InitOSKeymap (_THIS) {
slouken@390
    28
    const void *KCHRPtr;
slouken@390
    29
    UInt32 state;
slouken@390
    30
    UInt32 value;
slouken@390
    31
    int i;
slouken@390
    32
    int world = SDLK_WORLD_0;
slouken@47
    33
slouken@390
    34
    for ( i=0; i<SDL_TABLESIZE(keymap); ++i )
slouken@390
    35
        keymap[i] = SDLK_UNKNOWN;
slouken@390
    36
slouken@390
    37
    /* This keymap is almost exactly the same as the OS 9 one */
slouken@390
    38
    keymap[QZ_ESCAPE] = SDLK_ESCAPE;
slouken@390
    39
    keymap[QZ_F1] = SDLK_F1;
slouken@390
    40
    keymap[QZ_F2] = SDLK_F2;
slouken@390
    41
    keymap[QZ_F3] = SDLK_F3;
slouken@390
    42
    keymap[QZ_F4] = SDLK_F4;
slouken@390
    43
    keymap[QZ_F5] = SDLK_F5;
slouken@390
    44
    keymap[QZ_F6] = SDLK_F6;
slouken@390
    45
    keymap[QZ_F7] = SDLK_F7;
slouken@390
    46
    keymap[QZ_F8] = SDLK_F8;
slouken@390
    47
    keymap[QZ_F9] = SDLK_F9;
slouken@390
    48
    keymap[QZ_F10] = SDLK_F10;
slouken@390
    49
    keymap[QZ_F11] = SDLK_F11;
slouken@390
    50
    keymap[QZ_F12] = SDLK_F12;
slouken@390
    51
    keymap[QZ_PRINT] = SDLK_PRINT;
slouken@390
    52
    keymap[QZ_SCROLLOCK] = SDLK_SCROLLOCK;
slouken@390
    53
    keymap[QZ_PAUSE] = SDLK_PAUSE;
slouken@390
    54
    keymap[QZ_POWER] = SDLK_POWER;
slouken@390
    55
    keymap[QZ_BACKQUOTE] = SDLK_BACKQUOTE;
slouken@390
    56
    keymap[QZ_1] = SDLK_1;
slouken@390
    57
    keymap[QZ_2] = SDLK_2;
slouken@390
    58
    keymap[QZ_3] = SDLK_3;
slouken@390
    59
    keymap[QZ_4] = SDLK_4;
slouken@390
    60
    keymap[QZ_5] = SDLK_5;
slouken@390
    61
    keymap[QZ_6] = SDLK_6;
slouken@390
    62
    keymap[QZ_7] = SDLK_7;
slouken@390
    63
    keymap[QZ_8] = SDLK_8;
slouken@390
    64
    keymap[QZ_9] = SDLK_9;
slouken@390
    65
    keymap[QZ_0] = SDLK_0;
slouken@390
    66
    keymap[QZ_MINUS] = SDLK_MINUS;
slouken@390
    67
    keymap[QZ_EQUALS] = SDLK_EQUALS;
slouken@390
    68
    keymap[QZ_BACKSPACE] = SDLK_BACKSPACE;
slouken@390
    69
    keymap[QZ_INSERT] = SDLK_INSERT;
slouken@390
    70
    keymap[QZ_HOME] = SDLK_HOME;
slouken@390
    71
    keymap[QZ_PAGEUP] = SDLK_PAGEUP;
slouken@390
    72
    keymap[QZ_NUMLOCK] = SDLK_NUMLOCK;
slouken@390
    73
    keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS;
slouken@390
    74
    keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE;
slouken@390
    75
    keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
slouken@390
    76
    keymap[QZ_TAB] = SDLK_TAB;
slouken@390
    77
    keymap[QZ_q] = SDLK_q;
slouken@390
    78
    keymap[QZ_w] = SDLK_w;
slouken@390
    79
    keymap[QZ_e] = SDLK_e;
slouken@390
    80
    keymap[QZ_r] = SDLK_r;
slouken@390
    81
    keymap[QZ_t] = SDLK_t;
slouken@390
    82
    keymap[QZ_y] = SDLK_y;
slouken@390
    83
    keymap[QZ_u] = SDLK_u;
slouken@390
    84
    keymap[QZ_i] = SDLK_i;
slouken@390
    85
    keymap[QZ_o] = SDLK_o;
slouken@390
    86
    keymap[QZ_p] = SDLK_p;
slouken@390
    87
    keymap[QZ_LEFTBRACKET] = SDLK_LEFTBRACKET;
slouken@390
    88
    keymap[QZ_RIGHTBRACKET] = SDLK_RIGHTBRACKET;
slouken@390
    89
    keymap[QZ_BACKSLASH] = SDLK_BACKSLASH;
slouken@390
    90
    keymap[QZ_DELETE] = SDLK_DELETE;
slouken@390
    91
    keymap[QZ_END] = SDLK_END;
slouken@390
    92
    keymap[QZ_PAGEDOWN] = SDLK_PAGEDOWN;
slouken@390
    93
    keymap[QZ_KP7] = SDLK_KP7;
slouken@390
    94
    keymap[QZ_KP8] = SDLK_KP8;
slouken@390
    95
    keymap[QZ_KP9] = SDLK_KP9;
slouken@390
    96
    keymap[QZ_KP_MINUS] = SDLK_KP_MINUS;
slouken@390
    97
    keymap[QZ_CAPSLOCK] = SDLK_CAPSLOCK;
slouken@390
    98
    keymap[QZ_a] = SDLK_a;
slouken@390
    99
    keymap[QZ_s] = SDLK_s;
slouken@390
   100
    keymap[QZ_d] = SDLK_d;
slouken@390
   101
    keymap[QZ_f] = SDLK_f;
slouken@390
   102
    keymap[QZ_g] = SDLK_g;
slouken@390
   103
    keymap[QZ_h] = SDLK_h;
slouken@390
   104
    keymap[QZ_j] = SDLK_j;
slouken@390
   105
    keymap[QZ_k] = SDLK_k;
slouken@390
   106
    keymap[QZ_l] = SDLK_l;
slouken@390
   107
    keymap[QZ_SEMICOLON] = SDLK_SEMICOLON;
slouken@390
   108
    keymap[QZ_QUOTE] = SDLK_QUOTE;
slouken@390
   109
    keymap[QZ_RETURN] = SDLK_RETURN;
slouken@390
   110
    keymap[QZ_KP4] = SDLK_KP4;
slouken@390
   111
    keymap[QZ_KP5] = SDLK_KP5;
slouken@390
   112
    keymap[QZ_KP6] = SDLK_KP6;
slouken@390
   113
    keymap[QZ_KP_PLUS] = SDLK_KP_PLUS;
slouken@390
   114
    keymap[QZ_LSHIFT] = SDLK_LSHIFT;
slouken@390
   115
    keymap[QZ_z] = SDLK_z;
slouken@390
   116
    keymap[QZ_x] = SDLK_x;
slouken@390
   117
    keymap[QZ_c] = SDLK_c;
slouken@390
   118
    keymap[QZ_v] = SDLK_v;
slouken@390
   119
    keymap[QZ_b] = SDLK_b;
slouken@390
   120
    keymap[QZ_n] = SDLK_n;
slouken@390
   121
    keymap[QZ_m] = SDLK_m;
slouken@390
   122
    keymap[QZ_COMMA] = SDLK_COMMA;
slouken@390
   123
    keymap[QZ_PERIOD] = SDLK_PERIOD;
slouken@390
   124
    keymap[QZ_SLASH] = SDLK_SLASH;
slouken@390
   125
    keymap[QZ_UP] = SDLK_UP;
slouken@390
   126
    keymap[QZ_KP1] = SDLK_KP1;
slouken@390
   127
    keymap[QZ_KP2] = SDLK_KP2;
slouken@390
   128
    keymap[QZ_KP3] = SDLK_KP3;
slouken@390
   129
    keymap[QZ_KP_ENTER] = SDLK_KP_ENTER;
slouken@390
   130
    keymap[QZ_LCTRL] = SDLK_LCTRL;
slouken@390
   131
    keymap[QZ_LALT] = SDLK_LALT;
slouken@390
   132
    keymap[QZ_LMETA] = SDLK_LMETA;
slouken@390
   133
    keymap[QZ_SPACE] = SDLK_SPACE;
slouken@390
   134
    keymap[QZ_LEFT] = SDLK_LEFT;
slouken@390
   135
    keymap[QZ_DOWN] = SDLK_DOWN;
slouken@390
   136
    keymap[QZ_RIGHT] = SDLK_RIGHT;
slouken@390
   137
    keymap[QZ_KP0] = SDLK_KP0;
slouken@390
   138
    keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD;
slouken@390
   139
    keymap[QZ_IBOOK_ENTER] = SDLK_KP_ENTER;
slouken@390
   140
    keymap[QZ_IBOOK_RIGHT] = SDLK_RIGHT;
slouken@390
   141
    keymap[QZ_IBOOK_DOWN] = SDLK_DOWN;
slouken@390
   142
    keymap[QZ_IBOOK_UP]      = SDLK_UP;
slouken@390
   143
    keymap[QZ_IBOOK_LEFT] = SDLK_LEFT;
slouken@390
   144
slouken@501
   145
    /* 
slouken@501
   146
        Up there we setup a static scancode->keysym map. However, it will not
slouken@501
   147
        work very well on international keyboard. Hence we now query MacOS
slouken@501
   148
        for its own keymap to adjust our own mapping table. However, this is
slouken@501
   149
        basically only useful for ascii char keys. This is also the reason
slouken@501
   150
        why we keep the static table, too.
slouken@390
   151
     */
slouken@390
   152
slouken@390
   153
    /* Get a pointer to the systems cached KCHR */
slouken@390
   154
    KCHRPtr = (void *)GetScriptManagerVariable(smKCHRCache);
slouken@390
   155
    if (KCHRPtr)
slouken@390
   156
    {
slouken@390
   157
        /* Loop over all 127 possible scan codes */
slouken@390
   158
        for (i = 0; i < 0x7F; i++)
slouken@390
   159
        {
slouken@390
   160
            /* We pretend a clean start to begin with (i.e. no dead keys active */
slouken@390
   161
            state = 0;
slouken@390
   162
slouken@390
   163
            /* Now translate the key code to a key value */
slouken@390
   164
            value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
slouken@390
   165
slouken@390
   166
            /* If the state become 0, it was a dead key. We need to translate again,
slouken@390
   167
                passing in the new state, to get the actual key value */
slouken@390
   168
            if (state != 0)
slouken@390
   169
                value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
slouken@390
   170
slouken@390
   171
            /* Now we should have an ascii value, or 0. Try to figure out to which SDL symbol it maps */
slouken@390
   172
            if (value >= 128)     /* Some non-ASCII char, map it to SDLK_WORLD_* */
slouken@390
   173
                keymap[i] = world++;
slouken@390
   174
            else if (value >= 32)     /* non-control ASCII char */
slouken@390
   175
                keymap[i] = value;
slouken@390
   176
        }
slouken@390
   177
    }
slouken@390
   178
slouken@501
   179
    /* 
slouken@501
   180
        The keypad codes are re-setup here, because the loop above cannot
slouken@501
   181
        distinguish between a key on the keypad and a regular key. We maybe
slouken@501
   182
        could get around this problem in another fashion: NSEvent's flags
slouken@501
   183
        include a "NSNumericPadKeyMask" bit; we could check that and modify
slouken@501
   184
        the symbol we return on the fly. However, this flag seems to exhibit
slouken@501
   185
        some weird behaviour related to the num lock key
slouken@501
   186
    */
slouken@390
   187
    keymap[QZ_KP0] = SDLK_KP0;
slouken@390
   188
    keymap[QZ_KP1] = SDLK_KP1;
slouken@390
   189
    keymap[QZ_KP2] = SDLK_KP2;
slouken@390
   190
    keymap[QZ_KP3] = SDLK_KP3;
slouken@390
   191
    keymap[QZ_KP4] = SDLK_KP4;
slouken@390
   192
    keymap[QZ_KP5] = SDLK_KP5;
slouken@390
   193
    keymap[QZ_KP6] = SDLK_KP6;
slouken@390
   194
    keymap[QZ_KP7] = SDLK_KP7;
slouken@390
   195
    keymap[QZ_KP8] = SDLK_KP8;
slouken@390
   196
    keymap[QZ_KP9] = SDLK_KP9;
slouken@390
   197
    keymap[QZ_KP_MINUS] = SDLK_KP_MINUS;
slouken@390
   198
    keymap[QZ_KP_PLUS] = SDLK_KP_PLUS;
slouken@390
   199
    keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD;
slouken@390
   200
    keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS;
slouken@390
   201
    keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE;
slouken@390
   202
    keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
slouken@390
   203
    keymap[QZ_KP_ENTER] = SDLK_KP_ENTER;
slouken@47
   204
}
slouken@47
   205
slouken@501
   206
static void QZ_DoKey (_THIS, int state, NSEvent *event) {
slouken@47
   207
slouken@390
   208
    NSString *chars;
slouken@561
   209
    unsigned int numChars;
slouken@390
   210
    SDL_keysym key;
slouken@561
   211
    
slouken@501
   212
    /* 
slouken@561
   213
        A key event can contain multiple characters,
slouken@561
   214
        or no characters at all. In most cases, it
slouken@561
   215
        will contain a single character. If it contains
slouken@561
   216
        0 characters, we'll use 0 as the unicode. If it
slouken@561
   217
        contains multiple characters, we'll use 0 as
slouken@561
   218
        the scancode/keysym.
slouken@501
   219
    */
slouken@390
   220
    chars = [ event characters ];
slouken@561
   221
    numChars = [ chars length ];
slouken@561
   222
slouken@561
   223
    if (numChars == 1) {
slouken@390
   224
slouken@390
   225
        key.scancode = [ event keyCode ];
slouken@561
   226
        key.sym      = keymap [ key.scancode ];
slouken@561
   227
        key.unicode  = [ chars characterAtIndex:0 ];
slouken@561
   228
        key.mod      = KMOD_NONE;
slouken@390
   229
slouken@390
   230
        SDL_PrivateKeyboard (state, &key);
slouken@390
   231
    }
slouken@561
   232
    else if (numChars == 0) {
slouken@561
   233
      
slouken@561
   234
        key.scancode = [ event keyCode ];
slouken@561
   235
        key.sym      = keymap [ key.scancode ];
slouken@561
   236
        key.unicode  = 0;
slouken@561
   237
        key.mod      = KMOD_NONE;
slouken@561
   238
slouken@561
   239
        SDL_PrivateKeyboard (state, &key);
slouken@561
   240
    }
slouken@561
   241
    else /* (numChars > 1) */ {
slouken@561
   242
      
slouken@561
   243
        int i;
slouken@561
   244
        for (i = 0; i < numChars; i++) {
slouken@561
   245
slouken@561
   246
            key.scancode = 0;
slouken@561
   247
            key.sym      = 0;
slouken@561
   248
            key.unicode  = [ chars characterAtIndex:i];
slouken@561
   249
            key.mod      = KMOD_NONE;
slouken@561
   250
slouken@561
   251
            SDL_PrivateKeyboard (state, &key);
slouken@561
   252
        }
slouken@561
   253
    }
slouken@47
   254
}
slouken@47
   255
slouken@501
   256
static void QZ_DoModifiers (_THIS, unsigned int newMods) {
slouken@47
   257
slouken@390
   258
    const int mapping[] = { SDLK_CAPSLOCK, SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA } ;
slouken@47
   259
slouken@390
   260
    int i;
slouken@390
   261
    int bit;
slouken@390
   262
    SDL_keysym key;
slouken@47
   263
slouken@390
   264
    key.scancode = 0;
slouken@390
   265
    key.sym         = SDLK_UNKNOWN;
slouken@390
   266
    key.unicode     = 0;
slouken@390
   267
    key.mod         = KMOD_NONE;
slouken@172
   268
slouken@390
   269
    /* Iterate through the bits, testing each against the current modifiers */
slouken@390
   270
    for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
slouken@390
   271
slouken@390
   272
        unsigned int currentMask, newMask;
slouken@390
   273
slouken@501
   274
        currentMask = current_mods & bit;
slouken@501
   275
        newMask     = newMods & bit;
slouken@390
   276
slouken@390
   277
        if ( currentMask &&
slouken@390
   278
             currentMask != newMask ) {     /* modifier up event */
slouken@390
   279
slouken@390
   280
             key.sym = mapping[i];
slouken@390
   281
             /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
slouken@390
   282
             if (bit == NSAlphaShiftKeyMask)
slouken@390
   283
             SDL_PrivateKeyboard (SDL_PRESSED, &key);
slouken@390
   284
             SDL_PrivateKeyboard (SDL_RELEASED, &key);
slouken@390
   285
        }
slouken@390
   286
        else if ( newMask &&
slouken@390
   287
                  currentMask != newMask ) {     /* modifier down event */
slouken@390
   288
        
slouken@390
   289
             key.sym = mapping[i];
slouken@390
   290
             SDL_PrivateKeyboard (SDL_PRESSED, &key);
slouken@390
   291
             /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
slouken@390
   292
             if (bit == NSAlphaShiftKeyMask)
slouken@390
   293
                  SDL_PrivateKeyboard (SDL_RELEASED, &key);
slouken@390
   294
        }
slouken@390
   295
    }
slouken@390
   296
slouken@501
   297
    current_mods = newMods;
slouken@47
   298
}
slouken@47
   299
slouken@117
   300
static void QZ_DoActivate (_THIS)
slouken@117
   301
{
slouken@501
   302
    in_foreground = YES;
icculus@563
   303
    
icculus@563
   304
    /* Hide the mouse cursor if was hidden */
icculus@563
   305
    if (!cursor_visible) {
icculus@563
   306
        HideCursor ();
slouken@390
   307
    }
slouken@168
   308
icculus@563
   309
    /* Regrab input, only if it was previously grabbed */
icculus@563
   310
    if ( current_grab_mode == SDL_GRAB_ON ) {
icculus@563
   311
        
icculus@563
   312
        /* Restore cursor location if input was grabbed */
icculus@563
   313
        QZ_PrivateWarpCursor (this, cursor_loc.x, cursor_loc.y);
icculus@563
   314
        QZ_ChangeGrabState (this, QZ_ENABLE_GRAB);
slouken@390
   315
    }
slouken@390
   316
slouken@390
   317
    SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS);
slouken@47
   318
}
slouken@47
   319
slouken@47
   320
static void QZ_DoDeactivate (_THIS) {
slouken@47
   321
slouken@501
   322
    in_foreground = NO;
slouken@168
   323
icculus@563
   324
    /* Get the current cursor location, for restore on activate */
icculus@563
   325
    cursor_loc = [ NSEvent mouseLocation ]; /* global coordinates */
icculus@563
   326
    if (qz_window)
icculus@563
   327
        QZ_PrivateGlobalToLocal (this, &cursor_loc);
icculus@563
   328
    QZ_PrivateCocoaToSDL (this, &cursor_loc);
icculus@563
   329
    
icculus@563
   330
    /* Reassociate mouse and cursor */
icculus@563
   331
    CGAssociateMouseAndMouseCursorPosition (1);
slouken@390
   332
icculus@563
   333
    /* Show the cursor */
icculus@563
   334
    ShowCursor ();
slouken@390
   335
slouken@390
   336
    SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS);
slouken@47
   337
}
slouken@47
   338
slouken@555
   339
void QZ_SleepNotificationHandler (void * refcon,
slouken@555
   340
                                  io_service_t service,
slouken@555
   341
                                  natural_t messageType,
slouken@555
   342
                                  void * messageArgument )
slouken@555
   343
{
slouken@555
   344
     SDL_VideoDevice *this = (SDL_VideoDevice*)refcon;
slouken@555
   345
     
slouken@555
   346
     switch(messageType)
slouken@555
   347
     {
slouken@555
   348
         case kIOMessageSystemWillSleep:
icculus@563
   349
             IOAllowPowerChange(power_connection, (long) messageArgument);
slouken@555
   350
             break;
slouken@555
   351
         case kIOMessageCanSystemSleep:
icculus@563
   352
             IOAllowPowerChange(power_connection, (long) messageArgument);
slouken@555
   353
             break;
slouken@555
   354
         case kIOMessageSystemHasPoweredOn:
slouken@555
   355
			/* awake */
slouken@555
   356
            SDL_PrivateExpose();
slouken@555
   357
            break;
slouken@555
   358
     }
slouken@555
   359
}
slouken@555
   360
slouken@555
   361
static void QZ_RegisterForSleepNotifications (_THIS)
slouken@555
   362
{
slouken@555
   363
     CFRunLoopSourceRef rls;
slouken@555
   364
     IONotificationPortRef thePortRef;
slouken@555
   365
     io_object_t notifier;
slouken@555
   366
icculus@563
   367
     power_connection = IORegisterForSystemPower (this, &thePortRef, QZ_SleepNotificationHandler, &notifier);
slouken@555
   368
icculus@563
   369
     if (power_connection == 0)
slouken@555
   370
         NSLog(@"SDL: QZ_SleepNotificationHandler() IORegisterForSystemPower failed.");
slouken@555
   371
slouken@555
   372
     rls = IONotificationPortGetRunLoopSource (thePortRef);
slouken@555
   373
     CFRunLoopAddSource (CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
slouken@555
   374
     CFRelease (rls);
slouken@555
   375
}
slouken@555
   376
slouken@117
   377
static void QZ_PumpEvents (_THIS)
slouken@158
   378
{
slouken@435
   379
    int firstMouseEvent;
slouken@390
   380
    CGMouseDelta dx, dy;
slouken@390
   381
slouken@390
   382
    NSDate *distantPast;
slouken@390
   383
    NSEvent *event;
slouken@390
   384
    NSRect winRect;
slouken@390
   385
    NSRect titleBarRect;
slouken@390
   386
    NSAutoreleasePool *pool;
slouken@390
   387
slouken@390
   388
    pool = [ [ NSAutoreleasePool alloc ] init ];
slouken@390
   389
    distantPast = [ NSDate distantPast ];
slouken@390
   390
slouken@390
   391
    winRect = NSMakeRect (0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h);
slouken@435
   392
    titleBarRect = NSMakeRect (0, SDL_VideoSurface->h, SDL_VideoSurface->w,
slouken@435
   393
                                SDL_VideoSurface->h + 22);
slouken@435
   394
    
slouken@435
   395
    /* send the first mouse event in absolute coordinates */
slouken@435
   396
    firstMouseEvent = 1;
slouken@435
   397
    
slouken@435
   398
    /* accumulate any additional mouse moved events into one SDL mouse event */
slouken@390
   399
    dx = 0;
slouken@390
   400
    dy = 0;
slouken@390
   401
    
slouken@390
   402
    do {
slouken@390
   403
    
slouken@390
   404
        /* Poll for an event. This will not block */
slouken@390
   405
        event = [ NSApp nextEventMatchingMask:NSAnyEventMask
slouken@390
   406
                                    untilDate:distantPast
slouken@390
   407
                                    inMode: NSDefaultRunLoopMode dequeue:YES ];
slouken@390
   408
        if (event != nil) {
slouken@390
   409
slouken@390
   410
            unsigned int type;
slouken@390
   411
            BOOL isForGameWin;
icculus@563
   412
            BOOL isInGameWin;
icculus@563
   413
            
icculus@563
   414
            #define DO_MOUSE_DOWN(button) do {                                               \
slouken@501
   415
                            if ( in_foreground ) {                                           \
icculus@563
   416
                                if ( isInGameWin ) {                                         \
icculus@563
   417
                                    SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);      \
icculus@563
   418
                                    expect_mouse_up |= 1<<button;                            \
icculus@563
   419
                                }                                                            \
slouken@390
   420
                            }                                                                \
slouken@390
   421
                            else {                                                           \
slouken@390
   422
                                QZ_DoActivate (this);                                        \
slouken@390
   423
                            }                                                                \
slouken@390
   424
                            [ NSApp sendEvent:event ];                                       \
slouken@390
   425
            } while(0)
slouken@272
   426
            
icculus@563
   427
            #define DO_MOUSE_UP(button) do {                                            \
icculus@563
   428
                            if ( expect_mouse_up & (1<<button) ) {                      \
icculus@563
   429
                                SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);    \
icculus@563
   430
                                expect_mouse_up &= ~(1<<button);                        \
icculus@563
   431
                            }                                                           \
icculus@563
   432
                            [ NSApp sendEvent:event ];                                  \
slouken@390
   433
            } while(0)
slouken@390
   434
            
slouken@390
   435
            type = [ event type ];
slouken@390
   436
            isForGameWin = (qz_window == [ event window ]);
icculus@563
   437
            isInGameWin = NSPointInRect([event locationInWindow], winRect);
slouken@390
   438
            switch (type) {
slouken@390
   439
                case NSLeftMouseDown:
slouken@511
   440
                    if ( getenv("SDL_HAS3BUTTONMOUSE") ) {
icculus@563
   441
                        DO_MOUSE_DOWN (SDL_BUTTON_LEFT);
slouken@511
   442
                    } else {
slouken@511
   443
                        if ( NSCommandKeyMask & current_mods ) {
icculus@563
   444
                            last_virtual_button = SDL_BUTTON_RIGHT;
icculus@563
   445
                            DO_MOUSE_DOWN (SDL_BUTTON_RIGHT);
slouken@511
   446
                        }
slouken@511
   447
                        else if ( NSAlternateKeyMask & current_mods ) {
icculus@563
   448
                            last_virtual_button = SDL_BUTTON_MIDDLE;
icculus@563
   449
                            DO_MOUSE_DOWN (SDL_BUTTON_MIDDLE);
slouken@511
   450
                        }
slouken@511
   451
                        else {
icculus@563
   452
                            DO_MOUSE_DOWN (SDL_BUTTON_LEFT);
slouken@511
   453
                        }
slouken@390
   454
                    }
slouken@390
   455
                    break;
icculus@563
   456
                case NSOtherMouseDown: DO_MOUSE_DOWN (SDL_BUTTON_MIDDLE); break;
icculus@563
   457
                case NSRightMouseDown: DO_MOUSE_DOWN (SDL_BUTTON_RIGHT);  break;
slouken@390
   458
                case NSLeftMouseUp:
slouken@390
   459
                    if ( last_virtual_button != 0 ) {
icculus@563
   460
                        DO_MOUSE_UP (last_virtual_button);
slouken@390
   461
                        last_virtual_button = 0;
slouken@390
   462
                    }
slouken@390
   463
                    else {
icculus@563
   464
                        DO_MOUSE_UP (SDL_BUTTON_LEFT);
slouken@390
   465
                    }
slouken@390
   466
                    break;
icculus@563
   467
                case NSOtherMouseUp:   DO_MOUSE_UP (SDL_BUTTON_MIDDLE); break;
icculus@563
   468
                case NSRightMouseUp:   DO_MOUSE_UP (SDL_BUTTON_RIGHT);  break;
slouken@390
   469
                case NSSystemDefined:
slouken@501
   470
                    /*
slouken@501
   471
                        Future: up to 32 "mouse" buttons can be handled.
slouken@501
   472
                        if ([event subtype] == 7) {
slouken@501
   473
                            unsigned int buttons;
slouken@501
   474
                            buttons = [ event data2 ];
slouken@501
   475
                    */
slouken@390
   476
                    break;
slouken@390
   477
                case NSLeftMouseDragged:
slouken@390
   478
                case NSRightMouseDragged:
slouken@435
   479
                case NSOtherMouseDragged: /* usually middle mouse dragged */
slouken@390
   480
                case NSMouseMoved:
icculus@563
   481
                    if ( grab_state == QZ_INVISIBLE_GRAB ) {
slouken@272
   482
                
slouken@501
   483
                        /*
icculus@563
   484
                            If input is grabbed+hidden, the cursor doesn't move,
slouken@501
   485
                            so we have to call the lowlevel window server
slouken@501
   486
                            function. This is less accurate but works OK.                         
slouken@501
   487
                        */
slouken@390
   488
                        CGMouseDelta dx1, dy1;
slouken@390
   489
                        CGGetLastMouseDelta (&dx1, &dy1);
slouken@390
   490
                        dx += dx1;
slouken@390
   491
                        dy += dy1;
slouken@390
   492
                    }
slouken@435
   493
                    else if (firstMouseEvent) {
slouken@435
   494
                        
slouken@501
   495
                        /*
slouken@501
   496
                            Get the first mouse event in a possible
slouken@501
   497
                            sequence of mouse moved events. Since we
slouken@501
   498
                            use absolute coordinates, this serves to
slouken@501
   499
                            compensate any inaccuracy in deltas, and
slouken@501
   500
                            provides the first known mouse position,
slouken@501
   501
                            since everything after this uses deltas
slouken@501
   502
                        */
slouken@435
   503
                        NSPoint p = [ event locationInWindow ];
icculus@563
   504
                        QZ_PrivateCocoaToSDL (this, &p);
slouken@454
   505
                        SDL_PrivateMouseMotion (0, 0, p.x, p.y);
slouken@435
   506
                        firstMouseEvent = 0;
slouken@435
   507
                    }
slouken@435
   508
                    else {
slouken@435
   509
                    
slouken@501
   510
                        /*
slouken@501
   511
                            Get the amount moved since the last drag or move event,
slouken@501
   512
                            add it on for one big move event at the end.
slouken@501
   513
                        */
slouken@501
   514
                        dx += [ event deltaX ];
slouken@501
   515
                        dy += [ event deltaY ];
slouken@435
   516
                    }
icculus@563
   517
                    
icculus@563
   518
                    /* 
icculus@563
   519
                        Handle grab input+cursor visible by warping the cursor back
icculus@563
   520
                        into the game window. This still generates a mouse moved event,
icculus@563
   521
                        but not as a result of the warp (so it's in the right direction).
icculus@563
   522
                    */
icculus@563
   523
                    if ( grab_state == QZ_VISIBLE_GRAB &&
icculus@563
   524
                         !isInGameWin ) {
icculus@563
   525
                       
icculus@563
   526
                        NSPoint p = [ event locationInWindow ]; 
icculus@563
   527
                        QZ_PrivateCocoaToSDL (this, &p);
icculus@563
   528
icculus@563
   529
                        if ( p.x < 0.0 ) 
icculus@563
   530
                            p.x = 0.0;
icculus@563
   531
                        
icculus@563
   532
                        if ( p.y < 0.0 ) 
icculus@563
   533
                            p.y = 0.0;
icculus@563
   534
                        
icculus@563
   535
                        if ( p.x >= winRect.size.width ) 
icculus@563
   536
                            p.x = winRect.size.width-1;
icculus@563
   537
                        
icculus@563
   538
                        if ( p.y >= winRect.size.height ) 
icculus@563
   539
                            p.y = winRect.size.height-1;
icculus@563
   540
                        
icculus@563
   541
                        QZ_PrivateWarpCursor (this, p.x, p.y);
icculus@563
   542
                    }
slouken@390
   543
                    break;
slouken@390
   544
                case NSScrollWheel:
icculus@563
   545
                    if ( isInGameWin ) {
slouken@390
   546
                        float dy;
slouken@502
   547
                        Uint8 button;
slouken@390
   548
                        dy = [ event deltaY ];
slouken@390
   549
                        if ( dy > 0.0 ) /* Scroll up */
slouken@502
   550
                            button = SDL_BUTTON_WHEELUP;
slouken@390
   551
                        else /* Scroll down */
slouken@502
   552
                            button = SDL_BUTTON_WHEELDOWN;
icculus@563
   553
                        /* For now, wheel is sent as a quick down+up */
slouken@502
   554
                        SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);
slouken@502
   555
                        SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);
slouken@390
   556
                    }
slouken@390
   557
                    break;
slouken@390
   558
                case NSKeyUp:
slouken@501
   559
                    QZ_DoKey (this, SDL_RELEASED, event);
slouken@390
   560
                    break;
slouken@390
   561
                case NSKeyDown:
slouken@501
   562
                    QZ_DoKey (this, SDL_PRESSED, event);
slouken@390
   563
                    break;
slouken@390
   564
                case NSFlagsChanged:
slouken@501
   565
                    QZ_DoModifiers(this, [ event modifierFlags ] );
slouken@390
   566
                    break;
slouken@390
   567
                case NSAppKitDefined:
slouken@390
   568
                    switch ( [ event subtype ] ) {
slouken@390
   569
                        case NSApplicationActivatedEventType:
slouken@390
   570
                            QZ_DoActivate (this);
slouken@390
   571
                            break;
slouken@390
   572
                        case NSApplicationDeactivatedEventType:
slouken@390
   573
                            QZ_DoDeactivate (this);
slouken@390
   574
                            break;
slouken@390
   575
                    }
slouken@390
   576
                    [ NSApp sendEvent:event ];
slouken@390
   577
                    break;
slouken@390
   578
                    /* case NSApplicationDefined: break; */
slouken@390
   579
                    /* case NSPeriodic: break; */
slouken@390
   580
                    /* case NSCursorUpdate: break; */
slouken@390
   581
                default:
slouken@390
   582
                    [ NSApp sendEvent:event ];
slouken@272
   583
            }
slouken@272
   584
        }
slouken@390
   585
    } while (event != nil);
slouken@390
   586
    
slouken@435
   587
    /* handle accumulated mouse moved events */
slouken@390
   588
    if (dx != 0 || dy != 0)
slouken@435
   589
        SDL_PrivateMouseMotion (0, 1, dx, dy);
slouken@390
   590
    
slouken@390
   591
    [ pool release ];
slouken@502
   592
}