]> icculus.org git repositories - divverent/darkplaces.git/blob - vid_agl.c
stop complaining about SVC_PARTICLE
[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, GLint 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         GLenum error;
370
371         // FullScreen Display stuff
372         GDHandle gdhDisplay; // = GetMainDevice(); /*deprecated*/
373         CGDirectDisplayID mainDisplay = CGMainDisplayID();
374
375         if (!GL_OpenLibrary())
376         {
377                 Con_Printf("Unable to load GL driver\n");
378                 return false;
379         }
380
381         if ((qaglChoosePixelFormat = (AGLPixelFormat (*) (const AGLDevice *gdevs, GLint ndev, const GLint *attribList))GL_GetProcAddress("aglChoosePixelFormat")) == NULL
382          || (qaglCreateContext = (AGLContext (*) (AGLPixelFormat pix, AGLContext share))GL_GetProcAddress("aglCreateContext")) == NULL
383          || (qaglDestroyContext = (GLboolean (*) (AGLContext ctx))GL_GetProcAddress("aglDestroyContext")) == NULL
384          || (qaglDestroyPixelFormat = (void (*) (AGLPixelFormat pix))GL_GetProcAddress("aglDestroyPixelFormat")) == 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                 //  Set pixel format with built attribs
430                 //  Note: use NULL then must use 0
431                 pixelFormat = qaglChoosePixelFormat(NULL, 0, attributes);
432                 error = aglGetError();
433                 if (error != AGL_NO_ERROR)
434                 {
435                         Con_Printf("qaglChoosePixelFormat FAILED: %s\n",
436                                         (char *)aglErrorString(error));
437                         ReleaseWindow(window);
438                         return false;
439                 }
440         }
441
442         if (fullscreen)
443         {
444                 //  Output is FullScreen
445                 //  Get the mainDisplay and set resolution to current
446                 CGDisplayCapture (mainDisplay);
447                 CFDictionaryRef refDisplayMode = CGDisplayBestModeForParameters (mainDisplay, bpp, width, height, NULL);
448                 CGDisplaySwitchToMode (mainDisplay, refDisplayMode);
449                 DMGetGDeviceByDisplayID ((DisplayIDType)mainDisplay, &gdhDisplay, false);
450
451                 //  Set pixel format with built attribs
452                 //  Note: specifying a device is *required* for AGL_FullScreen
453                 pixelFormat = qaglChoosePixelFormat(&gdhDisplay, 1, attributes);
454                 error = aglGetError();
455                 if (error != AGL_NO_ERROR)
456                 {
457                         Con_Printf("qaglChoosePixelFormat FAILED: %s\n",
458                                                 (char *)aglErrorString(error));
459                         ReleaseWindow(window);
460                         return false;
461                 }
462         }
463
464         // Create a context using the pform
465         context = qaglCreateContext(pixelFormat, NULL);
466         error = aglGetError();
467         if (error != AGL_NO_ERROR)
468         {
469                 Con_Printf("qaglCreateContext FAILED: %s\n",
470                                         (char *)aglErrorString(error));
471         }
472
473         // Make the context the current one ('enable' it)
474         qaglSetCurrentContext(context);   
475         error = aglGetError();
476                 if (error != AGL_NO_ERROR)
477                 {
478                         Con_Printf("qaglSetCurrentContext FAILED: %s\n",
479                                                 (char *)aglErrorString(error));
480                         ReleaseWindow(window);
481                         return false;
482                 }
483
484         // Discard pform
485         qaglDestroyPixelFormat(pixelFormat);
486
487         // Attempt fullscreen if requested
488         if (fullscreen)
489         {
490                 qaglSetFullScreen (context, width, height, refreshrate, 0);
491                 error = aglGetError();
492                 if (error != AGL_NO_ERROR)
493                 {
494                         Con_Printf("qaglSetFullScreen FAILED: %s\n",
495                                                 (char *)aglErrorString(error));
496                         return false;
497                         // FullScreen Failed
498                         vid_isfullscreen = false;
499                 }
500                 else
501                 {
502                         // FullScreen Success
503                         vid_isfullscreen = true;
504                 }
505         }
506         
507         if (!fullscreen)
508         {
509         // Note: this _is_ a Window != FullScreen
510         // Set Window as Drawable
511         qaglSetDrawable(context, GetWindowPort(window));
512         error = aglGetError();
513                 if (error != AGL_NO_ERROR)
514                 {
515                         Con_Printf("qaglSetDrawable FAILED: %s\n",
516                                                 (char *)aglErrorString(error));
517                         ReleaseWindow(window);
518                         return false;
519                 }
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_usingmouse = false;
537         vid_hidden = false;
538         vid_activewindow = true;
539         GL_Init();
540
541         SelectWindow(window);
542         ShowWindow(window);
543
544         return true;
545 }
546
547 static void Handle_KeyMod(UInt32 keymod)
548 {
549         static UInt32 prev_keymod = 0;
550         UInt32 modChanges = prev_keymod ^ keymod;
551
552         if ((modChanges & cmdKey) != 0)
553         {
554                 Key_Event(K_AUX1, '\0', (keymod & cmdKey) != 0);
555         }
556         if ((modChanges & shiftKey) != 0 || (modChanges & rightShiftKey) != 0)
557         {
558                 Key_Event(K_SHIFT, '\0', (keymod & shiftKey) != 0);
559         }
560         if ((modChanges & alphaLock) != 0)
561         {
562                 Key_Event(K_CAPSLOCK, '\0', (keymod & alphaLock) != 0);
563         }
564         if ((modChanges & optionKey) != 0 || (modChanges & rightOptionKey) != 0)
565         {
566                 Key_Event(K_ALT, '\0', (keymod & optionKey) != 0);
567         }
568         if ((modChanges & controlKey) != 0 || (modChanges & rightControlKey) != 0)
569         {
570                 Key_Event(K_CTRL, '\0', (keymod & controlKey) != 0);
571         }
572         if ((modChanges & kEventKeyModifierNumLockMask) != 0)
573         {
574                 Key_Event(K_NUMLOCK, '\0', (keymod & kEventKeyModifierNumLockMask) != 0);
575         }
576         if ((modChanges & kEventKeyModifierFnMask) != 0)
577         {
578                 Key_Event(K_AUX2, '\0', (keymod & kEventKeyModifierFnMask) != 0);
579         }
580
581         prev_keymod = keymod;
582 }
583
584 static void Handle_Key(unsigned char charcode, qboolean keypressed)
585 {
586         unsigned int keycode = 0;
587         char ascii = '\0';
588
589         switch (charcode)
590         {
591                 case kHomeCharCode:
592                         keycode = K_HOME;
593                         break;
594                 case kEnterCharCode:
595                         keycode = K_KP_ENTER;
596                         break;
597                 case kEndCharCode:
598                         keycode = K_END;
599                         break;
600                 case kBackspaceCharCode:
601                         keycode = K_BACKSPACE;
602                         break;
603                 case kTabCharCode:
604                         keycode = K_TAB;
605                         break;
606                 case kPageUpCharCode:
607                         keycode = K_PGUP;
608                         break;
609                 case kPageDownCharCode:
610                         keycode = K_PGDN;
611                         break;
612                 case kReturnCharCode:
613                         keycode = K_ENTER;
614                         break;
615                 case kEscapeCharCode:
616                         keycode = K_ESCAPE;
617                         break;
618                 case kLeftArrowCharCode:
619                         keycode = K_LEFTARROW;
620                         break;
621                 case kRightArrowCharCode:
622                         keycode = K_RIGHTARROW;
623                         break;
624                 case kUpArrowCharCode:
625                         keycode = K_UPARROW;
626                         break;
627                 case kDownArrowCharCode :
628                         keycode = K_DOWNARROW;
629                         break;
630                 case kDeleteCharCode:
631                         keycode = K_DEL;
632                         break;
633                 case 0:
634                 case 191:
635                         // characters 0 and 191 are sent by the mouse buttons (?!)
636                         break;
637                 default:
638                         if ('A' <= charcode && charcode <= 'Z')
639                         {
640                                 keycode = charcode + ('a' - 'A');  // lowercase it
641                                 ascii = charcode;
642                         }
643                         else if (charcode >= 32)
644                         {
645                                 keycode = charcode;
646                                 ascii = charcode;
647                         }
648                         else
649                                 Con_Printf(">> UNKNOWN charcode: %d <<\n", charcode);
650         }
651
652         if (keycode != 0)
653                 Key_Event(keycode, ascii, keypressed);
654 }
655
656 void Sys_SendKeyEvents(void)
657 {
658         EventRef theEvent;
659         EventTargetRef theTarget;
660
661         // Start by processing the asynchronous events we received since the previous frame
662         VID_ProcessPendingAsyncEvents();
663
664         theTarget = GetEventDispatcherTarget();
665         while (ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &theEvent) == noErr)
666         {
667                 UInt32 eventClass = GetEventClass(theEvent);
668                 UInt32 eventKind = GetEventKind(theEvent);
669
670                 switch (eventClass)
671                 {
672                         case kEventClassMouse:
673                         {
674                                 EventMouseButton theButton;
675                                 int key;
676
677                                 switch (eventKind)
678                                 {
679                                         case kEventMouseDown:
680                                         case kEventMouseUp:
681                                                 GetEventParameter(theEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(theButton), NULL, &theButton);
682                                                 switch (theButton)
683                                                 {
684                                                         default:
685                                                         case kEventMouseButtonPrimary:
686                                                                 key = K_MOUSE1;
687                                                                 break;
688                                                         case kEventMouseButtonSecondary:
689                                                                 key = K_MOUSE2;
690                                                                 break;
691                                                         case kEventMouseButtonTertiary:
692                                                                 key = K_MOUSE3;
693                                                                 break;
694                                                 }
695                                                 Key_Event(key, '\0', eventKind == kEventMouseDown);
696                                                 break;
697
698                                         // Note: These two events are mutual exclusives
699                                         // Treat MouseDragged in the same statement, so we don't block MouseMoved while a mousebutton is held
700                                         case kEventMouseMoved:
701                                         case kEventMouseDragged:
702                                         {
703                                                 HIPoint deltaPos;
704
705                                                 GetEventParameter(theEvent, kEventParamMouseDelta, typeHIPoint, NULL, sizeof(deltaPos), NULL, &deltaPos);
706
707                                                 mouse_x += deltaPos.x;
708                                                 mouse_y += deltaPos.y;
709                                                 break;
710                                         }
711
712                                         case kEventMouseWheelMoved:
713                                         {
714                                                 SInt32 delta;
715                                                 unsigned int wheelEvent;
716
717                                                 GetEventParameter(theEvent, kEventParamMouseWheelDelta, typeSInt32, NULL, sizeof(delta), NULL, &delta);
718
719                                                 wheelEvent = (delta > 0) ? K_MWHEELUP : K_MWHEELDOWN;
720                                                 Key_Event(wheelEvent, 0, true);
721                                                 Key_Event(wheelEvent, 0, false);
722                                                 break;
723                                         }
724
725                                         default:
726                                                 Con_Printf (">> kEventClassMouse (UNKNOWN eventKind: %d) <<\n", eventKind);
727                                                 break;
728                                 }
729                         }
730
731                         case kEventClassKeyboard:
732                         {
733                                 char keycode;
734
735                                 switch (eventKind)
736                                 {
737                                         case kEventRawKeyDown:
738                                                 GetEventParameter(theEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(keycode), NULL, &keycode);
739                                                 Handle_Key(keycode, true);
740                                                 break;
741
742                                         case kEventRawKeyRepeat:
743                                                 break;
744
745                                         case kEventRawKeyUp:
746                                                 GetEventParameter(theEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(keycode), NULL, &keycode);
747                                                 Handle_Key(keycode, false);
748                                                 break;
749
750                                         case kEventRawKeyModifiersChanged:
751                                         {
752                                                 UInt32 keymod = 0;
753                                                 GetEventParameter(theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(keymod), NULL, &keymod);
754                                                 Handle_KeyMod(keymod);
755                                                 break;
756                                         }
757
758                                         case kEventHotKeyPressed:
759                                                 break;
760
761                                         case kEventHotKeyReleased:
762                                                 break;
763
764                                         case kEventMouseWheelMoved:
765                                                 break;
766
767                                         default:
768                                                 Con_Printf (">> kEventClassKeyboard (UNKNOWN eventKind: %d) <<\n", eventKind);
769                                                 break;
770                                 }
771                                 break;
772                         }
773
774                         case kEventClassTextInput:
775                                 Con_Printf(">> kEventClassTextInput (%d) <<\n", eventKind);
776                                 break;
777
778                         case kEventClassApplication:
779                                 switch (eventKind)
780                                 {
781                                         case kEventAppActivated :
782                                                 vid_activewindow = true;
783                                                 break;
784                                         case kEventAppDeactivated:
785                                                 vid_activewindow = false;
786                                                 VID_RestoreSystemGamma();
787                                                 break;
788                                         case kEventAppQuit:
789                                                 Sys_Quit();
790                                                 break;
791                                         case kEventAppActiveWindowChanged:
792                                                 break;
793                                         default:
794                                                 Con_Printf(">> kEventClassApplication (UNKNOWN eventKind: %d) <<\n", eventKind);
795                                                 break;
796                                 }
797                                 break;
798
799                         case kEventClassAppleEvent:
800                                 switch (eventKind)
801                                 {
802                                         case kEventAppleEvent :
803                                                 break;
804                                         default:
805                                                 Con_Printf(">> kEventClassAppleEvent (UNKNOWN eventKind: %d) <<\n", eventKind);
806                                                 break;
807                                 }
808                                 break;
809
810                         case kEventClassWindow:
811                                 switch (eventKind)
812                                 {
813                                         case kEventWindowUpdate :
814                                                 break;
815                                         default:
816                                                 Con_Printf(">> kEventClassWindow (UNKNOWN eventKind: %d) <<\n", eventKind);
817                                                 break;
818                                 }
819                                 break;
820
821                         case kEventClassControl:
822                                 break;
823
824                         default:
825                                 /*Con_Printf(">> UNKNOWN eventClass: %c%c%c%c, eventKind: %d <<\n",
826                                                         eventClass >> 24, (eventClass >> 16) & 0xFF,
827                                                         (eventClass >> 8) & 0xFF, eventClass & 0xFF,
828                                                         eventKind);*/
829                                 break;
830                 }
831
832                 SendEventToEventTarget (theEvent, theTarget);
833                 ReleaseEvent(theEvent);
834         }
835 }
836
837 void IN_Move (void)
838 {
839         if (mouse_avail)
840         {
841                 in_mouse_x = mouse_x;
842                 in_mouse_y = mouse_y;
843         }
844         mouse_x = 0;
845         mouse_y = 0;
846 }