changed VID_Finish to take an allowmousegrab parameter, this avoids mousegrab on...
[divverent/darkplaces.git] / vid_agl.c
1 /*
2         vid_agl.c
3
4         Mac OS X OpenGL and input module, using Carbon and AGL
5
6         Copyright (C) 2005  Mathieu Olivier
7
8         This program is free software; you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation; either version 2 of the License, or
11         (at your option) any later version.
12
13         This program is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with this program; if not, write to the Free Software
20         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 */
22
23
24 #include <dlfcn.h>
25 #include <signal.h>
26 #include <AGL/agl.h>
27 #include <Carbon/Carbon.h>
28 #include "quakedef.h"
29
30
31 // Tell startup code that we have a client
32 int cl_available = true;
33
34 qboolean vid_supportrefreshrate = true;
35
36 // AGL prototypes
37 AGLPixelFormat (*qaglChoosePixelFormat) (const AGLDevice *gdevs, GLint ndev, const GLint *attribList);
38 AGLContext (*qaglCreateContext) (AGLPixelFormat pix, AGLContext share);
39 GLboolean (*qaglDestroyContext) (AGLContext ctx);
40 void (*qaglDestroyPixelFormat) (AGLPixelFormat pix);
41 GLboolean (*qaglSetCurrentContext) (AGLContext ctx);
42 GLboolean (*qaglSetDrawable) (AGLContext ctx, AGLDrawable draw);
43 GLboolean (*qaglSetFullScreen) (AGLContext ctx, GLsizei width, GLsizei height, GLsizei freq, GLint device);
44 GLboolean (*qaglSetInteger) (AGLContext ctx, GLenum pname, const GLint *params);
45 void (*qaglSwapBuffers) (AGLContext ctx);
46
47 static qboolean mouse_avail = true;
48 static qboolean vid_usingmouse = false;
49 static float mouse_x, mouse_y;
50
51 static qboolean vid_isfullscreen = false;
52 static qboolean vid_usingvsync = false;
53
54 static int scr_width, scr_height;
55
56 static AGLContext context;
57 static  WindowRef window;
58
59
60 void VID_GetWindowSize (int *x, int *y, int *width, int *height)
61 {
62         *x = *y = 0;
63         *width = scr_width;
64         *height = scr_height;
65 }
66
67 static void IN_Activate( qboolean grab )
68 {
69         if (grab)
70         {
71                 if (!vid_usingmouse && mouse_avail && window)
72                 {
73                         Rect winBounds;
74                         CGPoint winCenter;
75
76                         SelectWindow(window);
77                         HideCursor();
78
79                         // Put the mouse cursor at the center of the window
80                         GetWindowBounds (window, kWindowContentRgn, &winBounds);
81                         winCenter.x = (winBounds.left + winBounds.right) / 2;
82                         winCenter.y = (winBounds.top + winBounds.bottom) / 2;
83                         CGWarpMouseCursorPosition(winCenter);
84
85                         // Lock the mouse pointer at its current position
86                         CGAssociateMouseAndMouseCursorPosition(false);
87
88                         mouse_x = mouse_y = 0;
89                         vid_usingmouse = true;
90                 }
91         }
92         else
93         {
94                 if (vid_usingmouse)
95                 {
96                         CGAssociateMouseAndMouseCursorPosition(true);
97                         ShowCursor();
98
99                         vid_usingmouse = false;
100                 }
101         }
102 }
103
104 void VID_Finish (qboolean allowmousegrab)
105 {
106         qboolean vid_usemouse;
107         qboolean vid_usevsync;
108
109         // handle the mouse state when windowed if that's changed
110         vid_usemouse = false;
111         if (allowmousgrab && vid_mouse.integer && !key_consoleactive && !cls.demoplayback)
112                 vid_usemouse = true;
113         if (!vid_activewindow)
114                 vid_usemouse = false;
115         if (vid_isfullscreen)
116                 vid_usemouse = true;
117         IN_Activate(vid_usemouse);
118
119         // handle changes of the vsync option
120         vid_usevsync = (vid_vsync.integer && !cls.timedemo);
121         if (vid_usingvsync != vid_usevsync)
122         {
123                 GLint sync = (vid_usevsync ? 1 : 0);
124
125                 if (qaglSetInteger(context, AGL_SWAP_INTERVAL, &sync) == GL_TRUE)
126                 {
127                         vid_usingvsync = vid_usevsync;
128                         Con_DPrintf("Vsync %s\n", vid_usevsync ? "activated" : "deactivated");
129                 }
130                 else
131                         Con_Printf("ERROR: can't %s vsync\n", vid_usevsync ? "activate" : "deactivate");
132         }
133
134         if (r_render.integer)
135         {
136                 if (r_speeds.integer || gl_finish.integer)
137                         qglFinish();
138                 qaglSwapBuffers(context);
139         }
140 }
141
142 #define GAMMA_TABLE_SIZE 256
143 int VID_SetGamma(unsigned short *ramps)
144 {
145         CGGammaValue table_red [GAMMA_TABLE_SIZE];
146         CGGammaValue table_green [GAMMA_TABLE_SIZE];
147         CGGammaValue table_blue [GAMMA_TABLE_SIZE];
148         unsigned int i;
149
150         // Convert the unsigned short table into 3 float tables
151         for (i = 0; i < GAMMA_TABLE_SIZE; i++)
152                 table_red[i] = (float)ramps[i] / 65535.0f;
153         for (i = 0; i < GAMMA_TABLE_SIZE; i++)
154                 table_green[i] = (float)ramps[i + GAMMA_TABLE_SIZE] / 65535.0f;
155         for (i = 0; i < GAMMA_TABLE_SIZE; i++)
156                 table_blue[i] = (float)ramps[i + 2 * GAMMA_TABLE_SIZE] / 65535.0f;
157
158         if (CGSetDisplayTransferByTable(CGMainDisplayID(), GAMMA_TABLE_SIZE, table_red, table_green, table_blue) != CGDisplayNoErr)
159         {
160                 Con_Print("VID_SetGamma: ERROR: CGSetDisplayTransferByTable failed!\n");
161                 return false;
162         }
163
164         return true;
165 }
166
167 int VID_GetGamma(unsigned short *ramps)
168 {
169         CGGammaValue table_red [GAMMA_TABLE_SIZE];
170         CGGammaValue table_green [GAMMA_TABLE_SIZE];
171         CGGammaValue table_blue [GAMMA_TABLE_SIZE];
172         CGTableCount actualsize = 0;
173         unsigned int i;
174
175         // Get the gamma ramps from the system
176         if (CGGetDisplayTransferByTable(CGMainDisplayID(), GAMMA_TABLE_SIZE, table_red, table_green, table_blue, &actualsize) != CGDisplayNoErr)
177         {
178                 Con_Print("VID_GetGamma: ERROR: CGGetDisplayTransferByTable failed!\n");
179                 return false;
180         }
181         if (actualsize != GAMMA_TABLE_SIZE)
182         {
183                 Con_Printf("VID_GetGamma: ERROR: invalid gamma table size (%u != %u)\n", actualsize, GAMMA_TABLE_SIZE);
184                 return false;
185         }
186
187         // Convert the 3 float tables into 1 unsigned short table
188         for (i = 0; i < GAMMA_TABLE_SIZE; i++)
189                 ramps[i] = table_red[i] * 65535.0f;
190         for (i = 0; i < GAMMA_TABLE_SIZE; i++)
191                 ramps[i + GAMMA_TABLE_SIZE] = table_green[i] * 65535.0f;
192         for (i = 0; i < GAMMA_TABLE_SIZE; i++)
193                 ramps[i + 2 * GAMMA_TABLE_SIZE] = table_blue[i] * 65535.0f;
194
195         return true;
196 }
197
198 void signal_handler(int sig)
199 {
200         printf("Received signal %d, exiting...\n", sig);
201         VID_RestoreSystemGamma();
202         Sys_Quit();
203         exit(0);
204 }
205
206 void InitSig(void)
207 {
208         signal(SIGHUP, signal_handler);
209         signal(SIGINT, signal_handler);
210         signal(SIGQUIT, signal_handler);
211         signal(SIGILL, signal_handler);
212         signal(SIGTRAP, signal_handler);
213         signal(SIGIOT, signal_handler);
214         signal(SIGBUS, signal_handler);
215         signal(SIGFPE, signal_handler);
216         signal(SIGSEGV, signal_handler);
217         signal(SIGTERM, signal_handler);
218 }
219
220 void VID_Init(void)
221 {
222         InitSig(); // trap evil signals
223 // COMMANDLINEOPTION: Input: -nomouse disables mouse support (see also vid_mouse cvar)
224         if (COM_CheckParm ("-nomouse") || COM_CheckParm("-safe"))
225                 mouse_avail = false;
226 }
227
228 static void *prjobj = NULL;
229
230 static void GL_CloseLibrary(void)
231 {
232         if (prjobj)
233                 dlclose(prjobj);
234         prjobj = NULL;
235         gl_driver[0] = 0;
236         gl_extensions = "";
237         gl_platform = "";
238         gl_platformextensions = "";
239 }
240
241 static int GL_OpenLibrary(void)
242 {
243         const char *name = "/System/Library/Frameworks/AGL.framework/AGL";
244
245         Con_Printf("Loading OpenGL driver %s\n", name);
246         GL_CloseLibrary();
247         if (!(prjobj = dlopen(name, RTLD_LAZY)))
248         {
249                 Con_Printf("Unable to open symbol list for %s\n", name);
250                 return false;
251         }
252         strcpy(gl_driver, name);
253         return true;
254 }
255
256 void *GL_GetProcAddress(const char *name)
257 {
258         return dlsym(prjobj, name);
259 }
260
261 void VID_Shutdown(void)
262 {
263         if (context == NULL || window == NULL)
264                 return;
265
266         IN_Activate(false);
267         VID_RestoreSystemGamma();
268
269         if (context != NULL)
270         {
271                 qaglDestroyContext(context);
272                 context = NULL;
273         }
274
275         if (window != NULL)
276         {
277                 DisposeWindow(window);
278                 window = NULL;
279         }
280
281         vid_hidden = true;
282         vid_isfullscreen = false;
283
284         GL_CloseLibrary();
285         Key_ClearStates ();
286 }
287
288 // Since the event handler can be called at any time, we store the events for later processing
289 static qboolean AsyncEvent_Quitting = false;
290 static qboolean AsyncEvent_Collapsed = false;
291 static OSStatus MainWindowEventHandler (EventHandlerCallRef nextHandler, EventRef event, void *userData)
292 {
293         OSStatus err = noErr;
294
295         switch (GetEventKind (event))
296         {
297                 case kEventWindowClosed:
298                         AsyncEvent_Quitting = true;
299                         break;
300
301                 // Docked (start)
302                 case kEventWindowCollapsing:
303                         AsyncEvent_Collapsed = true;
304                         break;
305
306                 // Undocked / restored (end)
307                 case kEventWindowExpanded:
308                         AsyncEvent_Collapsed = false;
309                         break;
310
311                 default:
312                         err = eventNotHandledErr;
313                         break;
314         }
315
316         return err;
317 }
318
319 static void VID_ProcessPendingAsyncEvents (void)
320 {
321         // Collapsed / expanded
322         if (AsyncEvent_Collapsed != vid_hidden)
323         {
324                 vid_hidden = !vid_hidden;
325                 vid_activewindow = false;
326                 VID_RestoreSystemGamma();
327         }
328
329         // Closed
330         if (AsyncEvent_Quitting)
331         {
332                 Sys_Quit();
333         }
334 }
335
336 static void VID_BuildAGLAttrib(GLint *attrib, int stencil, qboolean fullscreen)
337 {
338         *attrib++ = AGL_RGBA;
339         *attrib++ = AGL_RED_SIZE;*attrib++ = 1;
340         *attrib++ = AGL_GREEN_SIZE;*attrib++ = 1;
341         *attrib++ = AGL_BLUE_SIZE;*attrib++ = 1;
342         *attrib++ = AGL_DOUBLEBUFFER;
343         *attrib++ = AGL_DEPTH_SIZE;*attrib++ = 1;
344
345         // if stencil is enabled, ask for alpha too
346         if (stencil)
347         {
348                 *attrib++ = AGL_STENCIL_SIZE;*attrib++ = 8;
349                 *attrib++ = AGL_ALPHA_SIZE;*attrib++ = 1;
350         }
351         if (fullscreen)
352                 *attrib++ = AGL_FULLSCREEN;
353         *attrib++ = AGL_NONE;
354 }
355
356 int VID_InitMode(int fullscreen, int width, int height, int bpp, int refreshrate)
357 {
358     const EventTypeSpec winEvents[] =
359         {
360                 { kEventClassWindow, kEventWindowClosed },
361                 { kEventClassWindow, kEventWindowCollapsing },
362                 { kEventClassWindow, kEventWindowExpanded },
363         };
364         OSStatus carbonError;
365         Rect windowBounds;
366         GDHandle screen;
367         AGLPixelFormat pixelFormat;
368         GLint attributes [32];
369
370         if (!GL_OpenLibrary())
371         {
372                 Con_Printf("Unable to load GL driver\n");
373                 return false;
374         }
375
376         if ((qaglChoosePixelFormat = (AGLPixelFormat (*) (const AGLDevice *gdevs, GLint ndev, const GLint *attribList))GL_GetProcAddress("aglChoosePixelFormat")) == NULL
377          || (qaglCreateContext = (AGLContext (*) (AGLPixelFormat pix, AGLContext share))GL_GetProcAddress("aglCreateContext")) == NULL
378          || (qaglDestroyContext = (GLboolean (*) (AGLContext ctx))GL_GetProcAddress("aglDestroyContext")) == NULL
379          || (qaglDestroyPixelFormat = (void (*) (AGLPixelFormat pix))GL_GetProcAddress("aglDestroyPixelFormat")) == NULL
380          || (qaglSetCurrentContext = (GLboolean (*) (AGLContext ctx))GL_GetProcAddress("aglSetCurrentContext")) == NULL
381          || (qaglSetDrawable = (GLboolean (*) (AGLContext ctx, AGLDrawable draw))GL_GetProcAddress("aglSetDrawable")) == NULL
382          || (qaglSetFullScreen = (GLboolean (*) (AGLContext ctx, GLsizei width, GLsizei height, GLsizei freq, GLint device))GL_GetProcAddress("aglSetFullScreen")) == NULL
383          || (qaglSetInteger = (GLboolean (*) (AGLContext ctx, GLenum pname, const GLint *params))GL_GetProcAddress("aglSetInteger")) == NULL
384          || (qaglSwapBuffers = (void (*) (AGLContext ctx))GL_GetProcAddress("aglSwapBuffers")) == NULL
385         )
386         {
387                 Con_Printf("AGL functions not found\n");
388                 ReleaseWindow(window);
389                 return false;
390         }
391
392         // Ignore the events from the previous window
393         AsyncEvent_Quitting = false;
394         AsyncEvent_Collapsed = false;
395
396         // Create the window
397         SetRect(&windowBounds, 0, 0, width, height);
398         OffsetRect(&windowBounds, 100, 100);  // move it a bit towards the center of the screen
399         carbonError = CreateNewWindow(kDocumentWindowClass, kWindowStandardFloatingAttributes | kWindowStandardHandlerAttribute, &windowBounds, &window);
400         if (carbonError != noErr || window == NULL)
401         {
402                 Con_Printf("Unable to create window (error %d)\n", carbonError);
403                 return false;
404         }
405
406         // Set the window title
407         CFStringRef windowTitle = CFSTR("DarkPlaces AGL");
408         SetWindowTitleWithCFString(window, windowTitle);
409         CFRelease(windowTitle);
410
411         // Install the callback function for the window events we can't get
412         // through ReceiveNextEvent (i.e. close, collapse, and expand)
413         InstallWindowEventHandler (window, NewEventHandlerUPP (MainWindowEventHandler),
414                                                            GetEventTypeCount(winEvents), winEvents, window, NULL);
415
416         screen = GetGWorldDevice(GetWindowPort(window));
417         if (screen == NULL)
418         {
419                 Con_Printf("Unable to get GDevice for window\n");
420                 ReleaseWindow(window);
421                 return false;
422         }
423
424         // Create and set pixel format
425         VID_BuildAGLAttrib(attributes, bpp == 32, fullscreen);
426         pixelFormat = qaglChoosePixelFormat(&screen, 1, attributes);
427         if (pixelFormat == NULL)
428         {
429                 Con_Printf("Unable to create pixel format\n");
430                 ReleaseWindow(window);
431                 return false;
432         }
433
434         // Set context and show the window
435         context = qaglCreateContext(pixelFormat, NULL);
436         if (context == NULL)
437                 Sys_Error ("aglCreateContext failed");
438         if (fullscreen)
439         {
440                 if (!qaglSetFullScreen (context, width, height, refreshrate, 0))
441                         Sys_Error("aglSetFullScreen failed");
442                 vid_isfullscreen = true;
443         }
444         else
445         {
446                 if (!qaglSetDrawable(context, GetWindowPort(window)))
447                         Sys_Error ("aglSetDrawable failed");
448         }
449         if (!qaglSetCurrentContext(context))
450                 Sys_Error ("aglSetCurrentContext failed");
451         qaglDestroyPixelFormat(pixelFormat);
452
453         scr_width = width;
454         scr_height = height;
455
456         if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
457                 Sys_Error("glGetString not found in %s", gl_driver);
458
459         gl_renderer = (const char *)qglGetString(GL_RENDERER);
460         gl_vendor = (const char *)qglGetString(GL_VENDOR);
461         gl_version = (const char *)qglGetString(GL_VERSION);
462         gl_extensions = (const char *)qglGetString(GL_EXTENSIONS);
463         gl_platform = "AGL";
464         gl_videosyncavailable = true;
465
466         vid_usingmouse = false;
467         vid_hidden = false;
468         vid_activewindow = true;
469         GL_Init();
470
471         SelectWindow(window);
472         ShowWindow(window);
473
474         return true;
475 }
476
477 static void Handle_KeyMod(UInt32 keymod)
478 {
479         static UInt32 prev_keymod = 0;
480         UInt32 modChanges = prev_keymod ^ keymod;
481
482         if ((modChanges & cmdKey) != 0)
483         {
484                 Key_Event(K_AUX1, '\0', (keymod & cmdKey) != 0);
485         }
486         if ((modChanges & shiftKey) != 0 || (modChanges & rightShiftKey) != 0)
487         {
488                 Key_Event(K_SHIFT, '\0', (keymod & shiftKey) != 0);
489         }
490         if ((modChanges & alphaLock) != 0)
491         {
492                 Key_Event(K_CAPSLOCK, '\0', (keymod & alphaLock) != 0);
493         }
494         if ((modChanges & optionKey) != 0 || (modChanges & rightOptionKey) != 0)
495         {
496                 Key_Event(K_ALT, '\0', (keymod & optionKey) != 0);
497         }
498         if ((modChanges & controlKey) != 0 || (modChanges & rightControlKey) != 0)
499         {
500                 Key_Event(K_CTRL, '\0', (keymod & controlKey) != 0);
501         }
502         if ((modChanges & kEventKeyModifierNumLockMask) != 0)
503         {
504                 Key_Event(K_NUMLOCK, '\0', (keymod & kEventKeyModifierNumLockMask) != 0);
505         }
506         if ((modChanges & kEventKeyModifierFnMask) != 0)
507         {
508                 Key_Event(K_AUX2, '\0', (keymod & kEventKeyModifierFnMask) != 0);
509         }
510
511         prev_keymod = keymod;
512 }
513
514 static void Handle_Key(unsigned char charcode, qboolean keypressed)
515 {
516         unsigned int keycode = 0;
517         char ascii = '\0';
518
519         switch (charcode)
520         {
521                 case kHomeCharCode:
522                         keycode = K_HOME;
523                         break;
524                 case kEnterCharCode:
525                         keycode = K_KP_ENTER;
526                         break;
527                 case kEndCharCode:
528                         keycode = K_END;
529                         break;
530                 case kBackspaceCharCode:
531                         keycode = K_BACKSPACE;
532                         break;
533                 case kTabCharCode:
534                         keycode = K_TAB;
535                         break;
536                 case kPageUpCharCode:
537                         keycode = K_PGUP;
538                         break;
539                 case kPageDownCharCode:
540                         keycode = K_PGDN;
541                         break;
542                 case kReturnCharCode:
543                         keycode = K_ENTER;
544                         break;
545                 case kEscapeCharCode:
546                         keycode = K_ESCAPE;
547                         break;
548                 case kLeftArrowCharCode:
549                         keycode = K_LEFTARROW;
550                         break;
551                 case kRightArrowCharCode:
552                         keycode = K_RIGHTARROW;
553                         break;
554                 case kUpArrowCharCode:
555                         keycode = K_UPARROW;
556                         break;
557                 case kDownArrowCharCode :
558                         keycode = K_DOWNARROW;
559                         break;
560                 case kDeleteCharCode:
561                         keycode = K_DEL;
562                         break;
563                 case 0:
564                 case 191:
565                         // characters 0 and 191 are sent by the mouse buttons (?!)
566                         break;
567                 default:
568                         if ('A' <= charcode && charcode <= 'Z')
569                         {
570                                 keycode = charcode + ('a' - 'A');  // lowercase it
571                                 ascii = charcode;
572                         }
573                         else if (charcode >= 32)
574                         {
575                                 keycode = charcode;
576                                 ascii = charcode;
577                         }
578                         else
579                                 Con_Printf(">> UNKNOWN charcode: %d <<\n", charcode);
580         }
581
582         if (keycode != 0)
583                 Key_Event(keycode, ascii, keypressed);
584 }
585
586 void Sys_SendKeyEvents(void)
587 {
588         EventRef theEvent;
589         EventTargetRef theTarget;
590
591         // Start by processing the asynchronous events we received since the previous frame
592         VID_ProcessPendingAsyncEvents();
593
594         theTarget = GetEventDispatcherTarget();
595         while (ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &theEvent) == noErr)
596         {
597                 UInt32 eventClass = GetEventClass(theEvent);
598                 UInt32 eventKind = GetEventKind(theEvent);
599
600                 switch (eventClass)
601                 {
602                         case kEventClassMouse:
603                         {
604                                 EventMouseButton theButton;
605                                 int key;
606
607                                 switch (eventKind)
608                                 {
609                                         case kEventMouseDown:
610                                         case kEventMouseUp:
611                                                 GetEventParameter(theEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(theButton), NULL, &theButton);
612                                                 switch (theButton)
613                                                 {
614                                                         default:
615                                                         case kEventMouseButtonPrimary:
616                                                                 key = K_MOUSE1;
617                                                                 break;
618                                                         case kEventMouseButtonSecondary:
619                                                                 key = K_MOUSE2;
620                                                                 break;
621                                                         case kEventMouseButtonTertiary:
622                                                                 key = K_MOUSE3;
623                                                                 break;
624                                                 }
625                                                 Key_Event(key, '\0', eventKind == kEventMouseDown);
626                                                 break;
627
628                                         case kEventMouseMoved:
629                                         {
630                                                 HIPoint deltaPos;
631
632                                                 GetEventParameter(theEvent, kEventParamMouseDelta, typeHIPoint, NULL, sizeof(deltaPos), NULL, &deltaPos);
633
634                                                 mouse_x += deltaPos.x;
635                                                 mouse_y += deltaPos.y;
636                                                 break;
637                                         }
638
639                                         case kEventMouseWheelMoved:
640                                         {
641                                                 SInt32 delta;
642                                                 unsigned int wheelEvent;
643
644                                                 GetEventParameter(theEvent, kEventParamMouseWheelDelta, typeSInt32, NULL, sizeof(delta), NULL, &delta);
645
646                                                 wheelEvent = (delta > 0) ? K_MWHEELUP : K_MWHEELDOWN;
647                                                 Key_Event(wheelEvent, 0, true);
648                                                 Key_Event(wheelEvent, 0, false);
649                                                 break;
650                                         }
651
652                                         case kEventMouseDragged:
653                                                 break;
654
655                                         default:
656                                                 Con_Printf (">> kEventClassMouse (UNKNOWN eventKind: %d) <<\n", eventKind);
657                                                 break;
658                                 }
659                         }
660
661                         case kEventClassKeyboard:
662                         {
663                                 char keycode;
664
665                                 switch (eventKind)
666                                 {
667                                         case kEventRawKeyDown:
668                                                 GetEventParameter(theEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(keycode), NULL, &keycode);
669                                                 Handle_Key(keycode, true);
670                                                 break;
671
672                                         case kEventRawKeyRepeat:
673                                                 break;
674
675                                         case kEventRawKeyUp:
676                                                 GetEventParameter(theEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(keycode), NULL, &keycode);
677                                                 Handle_Key(keycode, false);
678                                                 break;
679
680                                         case kEventRawKeyModifiersChanged:
681                                         {
682                                                 UInt32 keymod = 0;
683                                                 GetEventParameter(theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(keymod), NULL, &keymod);
684                                                 Handle_KeyMod(keymod);
685                                                 break;
686                                         }
687
688                                         case kEventHotKeyPressed:
689                                                 break;
690
691                                         case kEventHotKeyReleased:
692                                                 break;
693
694                                         case kEventMouseWheelMoved:
695                                                 break;
696
697                                         default:
698                                                 Con_Printf (">> kEventClassKeyboard (UNKNOWN eventKind: %d) <<\n", eventKind);
699                                                 break;
700                                 }
701                                 break;
702                         }
703
704                         case kEventClassTextInput:
705                                 Con_Printf(">> kEventClassTextInput (%d) <<\n", eventKind);
706                                 break;
707
708                         case kEventClassApplication:
709                                 switch (eventKind)
710                                 {
711                                         case kEventAppActivated :
712                                                 vid_activewindow = true;
713                                                 break;
714                                         case kEventAppDeactivated:
715                                                 vid_activewindow = false;
716                                                 VID_RestoreSystemGamma();
717                                                 break;
718                                         case kEventAppQuit:
719                                                 Sys_Quit();
720                                                 break;
721                                         case kEventAppActiveWindowChanged:
722                                                 break;
723                                         default:
724                                                 Con_Printf(">> kEventClassApplication (UNKNOWN eventKind: %d) <<\n", eventKind);
725                                                 break;
726                                 }
727                                 break;
728
729                         case kEventClassAppleEvent:
730                                 switch (eventKind)
731                                 {
732                                         case kEventAppleEvent :
733                                                 break;
734                                         default:
735                                                 Con_Printf(">> kEventClassAppleEvent (UNKNOWN eventKind: %d) <<\n", eventKind);
736                                                 break;
737                                 }
738                                 break;
739
740                         case kEventClassWindow:
741                                 switch (eventKind)
742                                 {
743                                         case kEventWindowUpdate :
744                                                 break;
745                                         default:
746                                                 Con_Printf(">> kEventClassWindow (UNKNOWN eventKind: %d) <<\n", eventKind);
747                                                 break;
748                                 }
749                                 break;
750
751                         case kEventClassControl:
752                                 break;
753
754                         default:
755                                 /*Con_Printf(">> UNKNOWN eventClass: %c%c%c%c, eventKind: %d <<\n",
756                                                         eventClass >> 24, (eventClass >> 16) & 0xFF,
757                                                         (eventClass >> 8) & 0xFF, eventClass & 0xFF,
758                                                         eventKind);*/
759                                 break;
760                 }
761
762                 SendEventToEventTarget (theEvent, theTarget);
763                 ReleaseEvent(theEvent);
764         }
765 }
766
767 void IN_Move (void)
768 {
769         if (mouse_avail)
770         {
771                 in_mouse_x = mouse_x;
772                 in_mouse_y = mouse_y;
773         }
774         mouse_x = 0;
775         mouse_y = 0;
776 }