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