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