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