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