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