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