src/video/quartz/SDL_QuartzEvents.m
author Sam Lantinga <slouken@libsdl.org>
Tue, 04 Sep 2001 23:18:45 +0000
changeset 168 e92aa316c517
parent 158 4382c38dfbee
child 172 37e3ca9254c7
permissions -rw-r--r--
Added Max's patches for building MacOS X apps on command line
slouken@47
     1
/*
slouken@47
     2
    SDL - Simple DirectMedia Layer
slouken@47
     3
    Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
slouken@47
     4
slouken@47
     5
    This library is free software; you can redistribute it and/or
slouken@47
     6
    modify it under the terms of the GNU Library General Public
slouken@47
     7
    License as published by the Free Software Foundation; either
slouken@47
     8
    version 2 of the License, or (at your option) any later version.
slouken@47
     9
slouken@47
    10
    This library is distributed in the hope that it will be useful,
slouken@47
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@47
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@47
    13
    Library General Public License for more details.
slouken@47
    14
slouken@47
    15
    You should have received a copy of the GNU Library General Public
slouken@47
    16
    License along with this library; if not, write to the Free
slouken@47
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@47
    18
slouken@47
    19
    Sam Lantinga
slouken@47
    20
    slouken@devolution.com
slouken@47
    21
*/
slouken@47
    22
slouken@47
    23
#include "SDL_QuartzKeys.h"
slouken@47
    24
slouken@155
    25
static int last_virtual_button = 0; // Last virtual mouse button pressed
slouken@155
    26
slouken@47
    27
static void  QZ_InitOSKeymap (_THIS) {
slouken@47
    28
	int i;
slouken@47
    29
slouken@47
    30
	for ( i=0; i<SDL_TABLESIZE(keymap); ++i )
slouken@47
    31
		keymap[i] = SDLK_UNKNOWN;
slouken@47
    32
slouken@47
    33
	// This keymap is almost exactly the same as the OS 9 one
slouken@47
    34
        keymap[QZ_ESCAPE] = SDLK_ESCAPE;
slouken@47
    35
	keymap[QZ_F1] = SDLK_F1;
slouken@47
    36
	keymap[QZ_F2] = SDLK_F2;
slouken@47
    37
	keymap[QZ_F3] = SDLK_F3;
slouken@47
    38
	keymap[QZ_F4] = SDLK_F4;
slouken@47
    39
	keymap[QZ_F5] = SDLK_F5;
slouken@47
    40
	keymap[QZ_F6] = SDLK_F6;
slouken@47
    41
	keymap[QZ_F7] = SDLK_F7;
slouken@47
    42
	keymap[QZ_F8] = SDLK_F8;
slouken@47
    43
	keymap[QZ_F9] = SDLK_F9;
slouken@47
    44
	keymap[QZ_F10] = SDLK_F10;
slouken@47
    45
	keymap[QZ_F11] = SDLK_F11;
slouken@47
    46
	keymap[QZ_F12] = SDLK_F12;
slouken@47
    47
	keymap[QZ_PRINT] = SDLK_PRINT;
slouken@47
    48
	keymap[QZ_SCROLLOCK] = SDLK_SCROLLOCK;
slouken@47
    49
	keymap[QZ_PAUSE] = SDLK_PAUSE;
slouken@47
    50
	keymap[QZ_POWER] = SDLK_POWER;
slouken@47
    51
	keymap[QZ_BACKQUOTE] = SDLK_BACKQUOTE;
slouken@47
    52
	keymap[QZ_1] = SDLK_1;
slouken@47
    53
	keymap[QZ_2] = SDLK_2;
slouken@47
    54
	keymap[QZ_3] = SDLK_3;
slouken@47
    55
	keymap[QZ_4] = SDLK_4;
slouken@47
    56
	keymap[QZ_5] = SDLK_5;
slouken@47
    57
	keymap[QZ_6] = SDLK_6;
slouken@47
    58
	keymap[QZ_7] = SDLK_7;
slouken@47
    59
	keymap[QZ_8] = SDLK_8;
slouken@47
    60
	keymap[QZ_9] = SDLK_9;
slouken@47
    61
	keymap[QZ_0] = SDLK_0;
slouken@47
    62
	keymap[QZ_MINUS] = SDLK_MINUS;
slouken@47
    63
	keymap[QZ_EQUALS] = SDLK_EQUALS;
slouken@47
    64
	keymap[QZ_BACKSPACE] = SDLK_BACKSPACE;
slouken@47
    65
	keymap[QZ_INSERT] = SDLK_INSERT;
slouken@47
    66
	keymap[QZ_HOME] = SDLK_HOME;
slouken@47
    67
	keymap[QZ_PAGEUP] = SDLK_PAGEUP;
slouken@47
    68
	keymap[QZ_NUMLOCK] = SDLK_NUMLOCK;
slouken@47
    69
	keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS;
slouken@47
    70
	keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE;
slouken@47
    71
	keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
slouken@47
    72
	keymap[QZ_TAB] = SDLK_TAB;
slouken@47
    73
	keymap[QZ_q] = SDLK_q;
slouken@47
    74
	keymap[QZ_w] = SDLK_w;
slouken@47
    75
	keymap[QZ_e] = SDLK_e;
slouken@47
    76
	keymap[QZ_r] = SDLK_r;
slouken@47
    77
	keymap[QZ_t] = SDLK_t;
slouken@47
    78
	keymap[QZ_y] = SDLK_y;
slouken@47
    79
	keymap[QZ_u] = SDLK_u;
slouken@47
    80
	keymap[QZ_i] = SDLK_i;
slouken@47
    81
	keymap[QZ_o] = SDLK_o;
slouken@47
    82
	keymap[QZ_p] = SDLK_p;
slouken@47
    83
	keymap[QZ_LEFTBRACKET] = SDLK_LEFTBRACKET;
slouken@47
    84
	keymap[QZ_RIGHTBRACKET] = SDLK_RIGHTBRACKET;
slouken@47
    85
	keymap[QZ_BACKSLASH] = SDLK_BACKSLASH;
slouken@47
    86
	keymap[QZ_DELETE] = SDLK_DELETE;
slouken@47
    87
	keymap[QZ_END] = SDLK_END;
slouken@47
    88
	keymap[QZ_PAGEDOWN] = SDLK_PAGEDOWN;
slouken@47
    89
	keymap[QZ_KP7] = SDLK_KP7;
slouken@47
    90
	keymap[QZ_KP8] = SDLK_KP8;
slouken@47
    91
	keymap[QZ_KP9] = SDLK_KP9;
slouken@47
    92
	keymap[QZ_KP_MINUS] = SDLK_KP_MINUS;
slouken@47
    93
	keymap[QZ_CAPSLOCK] = SDLK_CAPSLOCK;
slouken@47
    94
	keymap[QZ_a] = SDLK_a;
slouken@47
    95
	keymap[QZ_s] = SDLK_s;
slouken@47
    96
	keymap[QZ_d] = SDLK_d;
slouken@47
    97
	keymap[QZ_f] = SDLK_f;
slouken@47
    98
	keymap[QZ_g] = SDLK_g;
slouken@47
    99
	keymap[QZ_h] = SDLK_h;
slouken@47
   100
	keymap[QZ_j] = SDLK_j;
slouken@47
   101
	keymap[QZ_k] = SDLK_k;
slouken@47
   102
	keymap[QZ_l] = SDLK_l;
slouken@47
   103
	keymap[QZ_SEMICOLON] = SDLK_SEMICOLON;
slouken@47
   104
	keymap[QZ_QUOTE] = SDLK_QUOTE;
slouken@47
   105
	keymap[QZ_RETURN] = SDLK_RETURN;
slouken@47
   106
	keymap[QZ_KP4] = SDLK_KP4;
slouken@47
   107
	keymap[QZ_KP5] = SDLK_KP5;
slouken@47
   108
	keymap[QZ_KP6] = SDLK_KP6;
slouken@47
   109
	keymap[QZ_KP_PLUS] = SDLK_KP_PLUS;
slouken@47
   110
	keymap[QZ_LSHIFT] = SDLK_LSHIFT;
slouken@47
   111
	keymap[QZ_z] = SDLK_z;
slouken@47
   112
	keymap[QZ_x] = SDLK_x;
slouken@47
   113
	keymap[QZ_c] = SDLK_c;
slouken@47
   114
	keymap[QZ_v] = SDLK_v;
slouken@47
   115
	keymap[QZ_b] = SDLK_b;
slouken@47
   116
	keymap[QZ_n] = SDLK_n;
slouken@47
   117
	keymap[QZ_m] = SDLK_m;
slouken@47
   118
	keymap[QZ_COMMA] = SDLK_COMMA;
slouken@47
   119
	keymap[QZ_PERIOD] = SDLK_PERIOD;
slouken@47
   120
	keymap[QZ_SLASH] = SDLK_SLASH;
slouken@47
   121
	keymap[QZ_UP] = SDLK_UP;
slouken@47
   122
	keymap[QZ_KP1] = SDLK_KP1;
slouken@47
   123
	keymap[QZ_KP2] = SDLK_KP2;
slouken@47
   124
	keymap[QZ_KP3] = SDLK_KP3;
slouken@47
   125
	keymap[QZ_KP_ENTER] = SDLK_KP_ENTER;
slouken@47
   126
	keymap[QZ_LCTRL] = SDLK_LCTRL;
slouken@47
   127
	keymap[QZ_LALT] = SDLK_LALT;
slouken@47
   128
	keymap[QZ_LMETA] = SDLK_LMETA;
slouken@47
   129
	keymap[QZ_SPACE] = SDLK_SPACE;
slouken@47
   130
	keymap[QZ_LEFT] = SDLK_LEFT;
slouken@47
   131
	keymap[QZ_DOWN] = SDLK_DOWN;
slouken@47
   132
	keymap[QZ_RIGHT] = SDLK_RIGHT;
slouken@47
   133
	keymap[QZ_KP0] = SDLK_KP0;
slouken@47
   134
	keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD;
slouken@47
   135
	keymap[QZ_IBOOK_ENTER] = SDLK_KP_ENTER;
slouken@47
   136
	keymap[QZ_IBOOK_RIGHT] = SDLK_RIGHT;
slouken@47
   137
	keymap[QZ_IBOOK_DOWN] = SDLK_DOWN;
slouken@47
   138
	keymap[QZ_IBOOK_UP]   = SDLK_UP;
slouken@47
   139
	keymap[QZ_IBOOK_LEFT] = SDLK_LEFT;
slouken@47
   140
}
slouken@47
   141
slouken@47
   142
static void QZ_DoKey (int state, NSEvent *event) {
slouken@47
   143
slouken@47
   144
        NSString *chars;
slouken@47
   145
        int i;
slouken@47
   146
        SDL_keysym key;
slouken@47
   147
        
slouken@47
   148
        /* An event can contain multiple characters */
slouken@47
   149
        /* I'll ignore this fact for now, since there is only one virtual key code per event */
slouken@47
   150
        chars = [ event characters ];
slouken@47
   151
        for (i =0; i < 1 /*[ chars length ] */; i++) {
slouken@47
   152
                    
slouken@47
   153
            key.scancode = [ event keyCode ];
slouken@47
   154
            key.sym      = keymap [ key.scancode ];
slouken@47
   155
            key.unicode  = [ chars characterAtIndex:i];
slouken@47
   156
            key.mod      = KMOD_NONE;
slouken@47
   157
                        
slouken@47
   158
            SDL_PrivateKeyboard (state, &key);
slouken@47
   159
        }
slouken@47
   160
}
slouken@47
   161
slouken@47
   162
static void QZ_DoModifiers (unsigned int newMods) {
slouken@47
   163
slouken@47
   164
    const int offset = 18;
slouken@47
   165
    const int mapping[] = { SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, 0, SDLK_LMETA } ;
slouken@47
   166
slouken@47
   167
    int bit;
slouken@47
   168
    SDL_keysym key;
slouken@47
   169
    key.scancode = 0;
slouken@47
   170
    key.sym      = SDLK_UNKNOWN;
slouken@47
   171
    key.unicode  = 0;
slouken@47
   172
    key.mod      = KMOD_NONE;
slouken@47
   173
    
slouken@47
   174
    /* Iterate through the bits, testing each against the current modifiers */
slouken@47
   175
    for (bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1) {
slouken@47
   176
    
slouken@47
   177
        unsigned int currentMask, newMask;
slouken@47
   178
        
slouken@47
   179
        currentMask = currentMods & bit;
slouken@47
   180
        newMask     = newMods & bit;
slouken@47
   181
        
slouken@47
   182
        if ( currentMask && 
slouken@47
   183
             currentMask != newMask ) {  /* modifier up event */
slouken@47
   184
slouken@47
   185
            key.sym = mapping[ currentMask >> offset ];
slouken@47
   186
            SDL_PrivateKeyboard (SDL_RELEASED, &key);
slouken@47
   187
        }
slouken@47
   188
        else
slouken@47
   189
        if ( newMask &&
slouken@47
   190
             currentMask != newMask ) {  /* modifier down event */
slouken@47
   191
         
slouken@47
   192
            key.sym = mapping [ newMask >> offset ];
slouken@47
   193
            SDL_PrivateKeyboard (SDL_PRESSED, &key);
slouken@47
   194
        }
slouken@47
   195
    }
slouken@47
   196
    
slouken@47
   197
    currentMods = newMods;
slouken@47
   198
}
slouken@47
   199
slouken@117
   200
static void QZ_DoActivate (_THIS)
slouken@117
   201
{
slouken@47
   202
    inForeground = YES;
slouken@47
   203
slouken@47
   204
    /* Regrab the mouse */
slouken@47
   205
    if (currentGrabMode == SDL_GRAB_ON) {
slouken@47
   206
        QZ_WarpWMCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2);
slouken@47
   207
        CGAssociateMouseAndMouseCursorPosition (0);
slouken@47
   208
    }
slouken@168
   209
slouken@168
   210
    /* Hide the mouse cursor if inside the app window */
slouken@168
   211
    // FIXME
slouken@168
   212
    if (!QZ_cursor_visible) {
slouken@168
   213
            HideCursor ();
slouken@168
   214
    }
slouken@47
   215
    
slouken@47
   216
    SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS);
slouken@47
   217
}
slouken@47
   218
slouken@47
   219
static void QZ_DoDeactivate (_THIS) {
slouken@47
   220
    
slouken@47
   221
    inForeground = NO;
slouken@47
   222
slouken@47
   223
    /* Ungrab mouse if it is grabbed */
slouken@47
   224
    if (currentGrabMode == SDL_GRAB_ON) {
slouken@47
   225
        CGAssociateMouseAndMouseCursorPosition (1);
slouken@47
   226
    }
slouken@168
   227
slouken@168
   228
    /* Show the mouse cursor */
slouken@168
   229
    // FIXME
slouken@168
   230
    if (!QZ_cursor_visible) {
slouken@168
   231
            ShowCursor ();
slouken@168
   232
    }
slouken@47
   233
    
slouken@47
   234
    SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS);
slouken@47
   235
}
slouken@47
   236
slouken@117
   237
static void QZ_PumpEvents (_THIS)
slouken@158
   238
{
slouken@155
   239
    NSDate *distantPast;
slouken@47
   240
    NSEvent *event;
slouken@47
   241
    NSRect winRect;
slouken@47
   242
    NSRect titleBarRect;
slouken@155
   243
    NSAutoreleasePool *pool;
slouken@155
   244
    
slouken@155
   245
    distantPast = [ [ NSDate distantPast ] retain ];
slouken@155
   246
    
slouken@155
   247
    pool = [ [ NSAutoreleasePool alloc ] init ];
slouken@155
   248
    
slouken@47
   249
    winRect = NSMakeRect (0, 0, SDL_VideoSurface->w + 1, SDL_VideoSurface->h + 1);
slouken@47
   250
    titleBarRect = NSMakeRect ( 0, SDL_VideoSurface->h, SDL_VideoSurface->w,
slouken@47
   251
        SDL_VideoSurface->h + 22 );
slouken@47
   252
            
slouken@47
   253
    do {
slouken@47
   254
    
slouken@47
   255
        /* Poll for an event. This will not block */
slouken@47
   256
        event = [ NSApp nextEventMatchingMask:NSAnyEventMask
slouken@47
   257
                    untilDate:distantPast
slouken@47
   258
                    inMode: NSDefaultRunLoopMode dequeue:YES ];
slouken@47
   259
    
slouken@47
   260
        if (event != nil) {
slouken@47
   261
            unsigned int type;
slouken@158
   262
            BOOL isForGameWin;
slouken@117
   263
slouken@158
   264
            #define DO_MOUSE_DOWN(button, sendToWindow) do {                 \
slouken@117
   265
                if ( inForeground ) {                                        \
slouken@117
   266
                    if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) ||       \
slouken@117
   267
                         NSPointInRect([event locationInWindow], winRect) )  \
slouken@117
   268
                        SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);  \
slouken@117
   269
                }                                                            \
slouken@117
   270
                else {                                                       \
slouken@117
   271
                    QZ_DoActivate (this);                                    \
slouken@158
   272
                }                                                            \
slouken@158
   273
                [ NSApp sendEvent:event ];                                   \
slouken@158
   274
                } while(0)
slouken@158
   275
                
slouken@158
   276
            #define DO_MOUSE_UP(button, sendToWindow) do {                   \
slouken@117
   277
                if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) ||           \
slouken@117
   278
                     !NSPointInRect([event locationInWindow], titleBarRect) )\
slouken@117
   279
                    SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);     \
slouken@158
   280
                [ NSApp sendEvent:event ];                                   \
slouken@158
   281
                } while(0)
slouken@158
   282
slouken@117
   283
            type = [ event type ];
slouken@158
   284
            isForGameWin = (qz_window == [ event window ]);
slouken@117
   285
            switch (type) {
slouken@47
   286
            
slouken@47
   287
            case NSLeftMouseDown:  
slouken@47
   288
                if ( NSCommandKeyMask & currentMods ) {
slouken@155
   289
                    last_virtual_button = 3;
slouken@155
   290
                    DO_MOUSE_DOWN (3, 0);
slouken@47
   291
                } 
slouken@47
   292
                else if ( NSAlternateKeyMask & currentMods ) {
slouken@155
   293
                    last_virtual_button = 2;
slouken@47
   294
                    DO_MOUSE_DOWN (2, 0);
slouken@47
   295
                } 
slouken@47
   296
                else {
slouken@47
   297
                    DO_MOUSE_DOWN (1, 1);
slouken@47
   298
                }
slouken@47
   299
                break;
slouken@47
   300
            case 25:               DO_MOUSE_DOWN (2, 0); break;
slouken@47
   301
            case NSRightMouseDown: DO_MOUSE_DOWN (3, 0); break;   
slouken@47
   302
            case NSLeftMouseUp:    
slouken@155
   303
            
slouken@155
   304
                if ( last_virtual_button != 0 ) {
slouken@155
   305
                    DO_MOUSE_UP (last_virtual_button, 0);
slouken@155
   306
                    last_virtual_button = 0;
slouken@155
   307
                }
slouken@155
   308
                else {
slouken@47
   309
                    DO_MOUSE_UP (1, 1);
slouken@155
   310
                }
slouken@47
   311
                break;
slouken@47
   312
            case 26:               DO_MOUSE_UP (2, 0);   break;
slouken@47
   313
            case NSRightMouseUp:   DO_MOUSE_UP (3, 0);   break;
slouken@47
   314
            case NSSystemDefined:
slouken@47
   315
                //if ([event subtype] == 7) {
slouken@47
   316
                //    unsigned int buttons;   // up to 32 mouse button states!
slouken@47
   317
                //    buttons = [ event data2 ];
slouken@47
   318
                //}
slouken@47
   319
                break;
slouken@47
   320
            case NSLeftMouseDragged:
slouken@47
   321
            case NSRightMouseDragged:
slouken@47
   322
            case 27:
slouken@47
   323
            case NSMouseMoved:
slouken@168
   324
                if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN)
slouken@168
   325
                	|| NSPointInRect([event locationInWindow], winRect) )
slouken@47
   326
                {
slouken@47
   327
                   static int moves = 0;
slouken@117
   328
                   NSPoint p;
slouken@117
   329
            
slouken@117
   330
                   if ( SDL_VideoSurface->flags & SDL_FULLSCREEN ) {
slouken@117
   331
                       p = [ NSEvent mouseLocation ];
slouken@117
   332
                       p.y = [[NSScreen mainScreen] frame].size.height - p.y;
slouken@117
   333
                   } else {
slouken@117
   334
            	       p = [ event locationInWindow ];
slouken@117
   335
                       p.y = SDL_VideoSurface->h - p.y;
slouken@117
   336
                   }
slouken@117
   337
slouken@47
   338
                   if ( (moves % 10) == 0 ) {
slouken@117
   339
                        SDL_PrivateMouseMotion (0, 0, p.x, p.y);
slouken@47
   340
                   }
slouken@47
   341
                   else {
slouken@47
   342
                        CGMouseDelta dx, dy;
slouken@47
   343
                        CGGetLastMouseDelta (&dx, &dy);
slouken@117
   344
                        SDL_PrivateMouseMotion (0, 1, dx, dy);
slouken@47
   345
                   }
slouken@117
   346
                   moves++;
slouken@47
   347
                }
slouken@47
   348
                break;
slouken@47
   349
            case NSScrollWheel:
slouken@47
   350
                {
slouken@117
   351
                    if (NSPointInRect([ event locationInWindow ], winRect)) {
slouken@47
   352
                        float dy;
slouken@47
   353
                        dy = [ event deltaY ];
slouken@47
   354
                        if ( dy > 0.0 ) /* Scroll up */
slouken@117
   355
                            SDL_PrivateMouseButton (SDL_PRESSED, 4, 0, 0);
slouken@47
   356
                        else /* Scroll down */
slouken@117
   357
                            SDL_PrivateMouseButton (SDL_PRESSED, 5, 0, 0);
slouken@47
   358
                    }
slouken@47
   359
                }
slouken@47
   360
                break;
slouken@47
   361
            case NSKeyUp:
slouken@47
   362
                QZ_DoKey (SDL_RELEASED, event);
slouken@47
   363
                break;
slouken@47
   364
            case NSKeyDown:
slouken@47
   365
                QZ_DoKey (SDL_PRESSED, event);
slouken@47
   366
                break;
slouken@47
   367
            case NSFlagsChanged:
slouken@47
   368
                QZ_DoModifiers( [ event modifierFlags ] );
slouken@47
   369
                break;
slouken@158
   370
//            case NSMouseEntered: break;
slouken@158
   371
//            case NSMouseExited: break;
slouken@47
   372
            case NSAppKitDefined:
slouken@47
   373
                switch ( [ event subtype ] ) {
slouken@47
   374
                case NSApplicationActivatedEventType:
slouken@117
   375
                    QZ_DoActivate (this);
slouken@47
   376
                    break;
slouken@47
   377
                case NSApplicationDeactivatedEventType:
slouken@47
   378
                    QZ_DoDeactivate (this);
slouken@47
   379
                    break;
slouken@47
   380
                }
slouken@158
   381
                [ NSApp sendEvent:event ];
slouken@47
   382
                break;
slouken@158
   383
//            case NSApplicationDefined: break;
slouken@158
   384
//            case NSPeriodic: break;
slouken@158
   385
//            case NSCursorUpdate: break;
slouken@158
   386
            default:
slouken@158
   387
                [ NSApp sendEvent:event ];
slouken@47
   388
            }
slouken@47
   389
        }
slouken@47
   390
      } while (event != nil);
slouken@155
   391
      
slouken@155
   392
      [ pool release ];
slouken@155
   393
      [ distantPast release ];
slouken@47
   394
}
slouken@47
   395