1/*************************************************************************/
2/*  os_osx.mm                                                            */
3/*************************************************************************/
4/*                       This file is part of:                           */
5/*                           GODOT ENGINE                                */
6/*                      https://godotengine.org                          */
7/*************************************************************************/
8/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10/*                                                                       */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the       */
13/* "Software"), to deal in the Software without restriction, including   */
14/* without limitation the rights to use, copy, modify, merge, publish,   */
15/* distribute, sublicense, and/or sell copies of the Software, and to    */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions:                                             */
18/*                                                                       */
19/* The above copyright notice and this permission notice shall be        */
20/* included in all copies or substantial portions of the Software.       */
21/*                                                                       */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29/*************************************************************************/
30#include "os_osx.h"
31
32#include "dir_access_osx.h"
33#include "drivers/gles2/rasterizer_instance_gles2.h"
34#include "main/main.h"
35#include "os/keyboard.h"
36#include "print_string.h"
37#include "scene/resources/texture.h"
38#include "sem_osx.h"
39#include "servers/physics/physics_server_sw.h"
40#include "servers/visual/visual_server_raster.h"
41#include "servers/visual/visual_server_wrap_mt.h"
42
43#include <Carbon/Carbon.h>
44#import <Cocoa/Cocoa.h>
45#include <IOKit/IOCFPlugIn.h>
46#include <IOKit/IOKitLib.h>
47#include <IOKit/hid/IOHIDKeys.h>
48#include <IOKit/hid/IOHIDLib.h>
49#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
50#include <os/log.h>
51#endif
52
53#include <fcntl.h>
54#include <libproc.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <sys/stat.h>
58#include <sys/types.h>
59#include <unistd.h>
60
61//uses portions of glfw
62
63//========================================================================
64// GLFW 3.0 - www.glfw.org
65//------------------------------------------------------------------------
66// Copyright (c) 2002-2006 Marcus Geelnard
67// Copyright (c) 2006-2010 Camilla Berglund <elmindreda@elmindreda.org>
68//
69// This software is provided 'as-is', without any express or implied
70// warranty. In no event will the authors be held liable for any damages
71// arising from the use of this software.
72//
73// Permission is granted to anyone to use this software for any purpose,
74// including commercial applications, and to alter it and redistribute it
75// freely, subject to the following restrictions:
76//
77// 1. The origin of this software must not be misrepresented; you must not
78//    claim that you wrote the original software. If you use this software
79//    in a product, an acknowledgment in the product documentation would
80//    be appreciated but is not required.
81//
82// 2. Altered source versions must be plainly marked as such, and must not
83//    be misrepresented as being the original software.
84//
85// 3. This notice may not be removed or altered from any source
86//    distribution.
87//
88//========================================================================
89
90#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
91#define NSWindowStyleMaskBorderless NSBorderlessWindowMask
92#endif
93
94static NSRect convertRectToBacking(NSRect contentRect) {
95
96#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
97	if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6)
98		return [OS_OSX::singleton->window_view convertRectToBacking:contentRect];
99	else
100#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
101		return contentRect;
102}
103
104static InputModifierState translateFlags(NSUInteger flags) {
105	InputModifierState mod;
106
107	mod.shift = (flags & NSShiftKeyMask);
108	mod.control = (flags & NSControlKeyMask);
109	mod.alt = (flags & NSAlternateKeyMask);
110	mod.meta = (flags & NSCommandKeyMask);
111
112	return mod;
113}
114
115static int mouse_x = 0;
116static int mouse_y = 0;
117static int prev_mouse_x = 0;
118static int prev_mouse_y = 0;
119static int button_mask = 0;
120static bool mouse_down_control = false;
121
122@interface GodotApplication : NSApplication
123@end
124
125@implementation GodotApplication
126
127// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
128// This works around an AppKit bug, where key up events while holding
129// down the command key don't get sent to the key window.
130- (void)sendEvent:(NSEvent *)event {
131	if ([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask))
132		[[self keyWindow] sendEvent:event];
133	else
134		[super sendEvent:event];
135}
136
137@end
138
139@interface GodotApplicationDelegate : NSObject
140@end
141
142@implementation GodotApplicationDelegate
143
144- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
145	if (OS_OSX::singleton->get_main_loop())
146		OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
147
148	return NSTerminateCancel;
149}
150
151- (void)applicationDidHide:(NSNotification *)notification {
152	/*
153	_Godotwindow* window;
154
155	for (window = _Godot.windowListHead;  window;  window = window->next)
156		_GodotInputWindowVisibility(window, GL_FALSE);
157*/
158}
159
160- (void)applicationDidUnhide:(NSNotification *)notification {
161	/*
162	_Godotwindow* window;
163
164	for (window = _Godot.windowListHead;  window;  window = window->next)
165	{
166		if ([window_object isVisible])
167			_GodotInputWindowVisibility(window, GL_TRUE);
168	}
169*/
170}
171
172- (void)applicationDidChangeScreenParameters:(NSNotification *)notification {
173	//_GodotInputMonitorChange();
174}
175
176@end
177
178@interface GodotWindowDelegate : NSObject {
179	//_Godotwindow* window;
180}
181
182@end
183
184@implementation GodotWindowDelegate
185
186- (BOOL)windowShouldClose:(id)sender {
187	//_GodotInputWindowCloseRequest(window);
188	if (OS_OSX::singleton->get_main_loop())
189		OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
190	return NO;
191}
192
193#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
194- (void)windowDidEnterFullScreen:(NSNotification *)notification {
195	OS_OSX::singleton->zoomed = true;
196}
197
198- (void)windowDidExitFullScreen:(NSNotification *)notification {
199	OS_OSX::singleton->zoomed = false;
200}
201#endif // MAC_OS_X_VERSION_MAX_ALLOWED
202
203- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
204	if (!OS_OSX::singleton)
205		return;
206
207	NSWindow *window = (NSWindow *)[notification object];
208	CGFloat newBackingScaleFactor = [window backingScaleFactor];
209	CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
210
211	if (newBackingScaleFactor != oldBackingScaleFactor) {
212		//Set new display scale and window size
213		OS_OSX::singleton->display_scale = newBackingScaleFactor;
214
215		const NSRect contentRect = [OS_OSX::singleton->window_view frame];
216		const NSRect fbRect = contentRect; //convertRectToBacking(contentRect);
217
218		OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale;
219		OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale;
220
221		//Update context
222		if (OS_OSX::singleton->main_loop) {
223			[OS_OSX::singleton->context update];
224
225			//Force window resize ???
226			NSRect frame = [OS_OSX::singleton->window_object frame];
227			[OS_OSX::singleton->window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, 1, 1) display:YES];
228			[OS_OSX::singleton->window_object setFrame:frame display:YES];
229		}
230	}
231}
232
233- (void)windowDidResize:(NSNotification *)notification {
234	[OS_OSX::singleton->context update];
235
236	const NSRect contentRect = [OS_OSX::singleton->window_view frame];
237	const NSRect fbRect = contentRect; //convertRectToBacking(contentRect);
238
239	OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale;
240	OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale;
241
242	if (OS_OSX::singleton->main_loop) {
243		Main::force_redraw();
244		//Event retrieval blocks until resize is over. Call Main::iteration() directly.
245		Main::iteration();
246	}
247
248	//_GodotInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
249	//_GodotInputWindowSize(window, contentRect.size.width, contentRect.size.height);
250	//_GodotInputWindowDamage(window);
251
252	//if (window->cursorMode == Godot_CURSOR_DISABLED)
253	//	centerCursor(window);
254}
255
256- (void)windowDidMove:(NSNotification *)notification {
257	//[window->nsgl.context update];
258
259	//int x, y;
260	//_GodotPlatformGetWindowPos(window, &x, &y);
261	//_GodotInputWindowPos(window, x, y);
262
263	//if (window->cursorMode == Godot_CURSOR_DISABLED)
264	//	centerCursor(window);
265}
266
267- (void)windowDidBecomeKey:(NSNotification *)notification {
268	//_GodotInputWindowFocus(window, GL_TRUE);
269	//_GodotPlatformSetCursorMode(window, window->cursorMode);
270	if (OS_OSX::singleton->get_main_loop())
271		OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
272}
273
274- (void)windowDidResignKey:(NSNotification *)notification {
275	//_GodotInputWindowFocus(window, GL_FALSE);
276	//_GodotPlatformSetCursorMode(window, Godot_CURSOR_NORMAL);
277	if (OS_OSX::singleton->get_main_loop())
278		OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
279}
280
281- (void)windowDidMiniaturize:(NSNotification *)notification {
282	OS_OSX::singleton->wm_minimized(true);
283	if (OS_OSX::singleton->get_main_loop())
284		OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
285};
286
287- (void)windowDidDeminiaturize:(NSNotification *)notification {
288	OS_OSX::singleton->wm_minimized(false);
289	if (OS_OSX::singleton->get_main_loop())
290		OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
291};
292
293@end
294
295@interface GodotContentView : NSView {
296	NSTrackingArea *trackingArea;
297}
298
299@end
300
301@implementation GodotContentView
302
303+ (void)initialize {
304	if (self == [GodotContentView class]) {
305		/*
306		if (_glfw.ns.cursor == nil) {
307			NSImage* data = [[NSImage alloc] initWithSize:NSMakeSize(1, 1)];
308			_glfw.ns.cursor = [[NSCursor alloc] initWithImage:data hotSpot:NSZeroPoint];
309			[data release];
310		}
311*/
312	}
313}
314
315- (id)init {
316	self = [super init];
317	trackingArea = nil;
318	[self updateTrackingAreas];
319	[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
320	return self;
321}
322
323- (void)dealloc {
324	[trackingArea release];
325	[super dealloc];
326}
327
328- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
329	return NSDragOperationCopy;
330}
331
332- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
333	return NSDragOperationCopy;
334}
335
336- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
337	NSPasteboard *pboard = [sender draggingPasteboard];
338	NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
339
340	Vector<String> files;
341	for (int i = 0; i < filenames.count; i++) {
342		NSString *ns = [filenames objectAtIndex:i];
343		char *utfs = strdup([ns UTF8String]);
344		String ret;
345		ret.parse_utf8(utfs);
346		free(utfs);
347		files.push_back(ret);
348	}
349
350	if (files.size()) {
351		OS_OSX::singleton->main_loop->drop_files(files, 0);
352		OS_OSX::singleton->move_window_to_foreground();
353	}
354
355	return NO;
356}
357
358- (BOOL)isOpaque {
359	return YES;
360}
361
362- (BOOL)canBecomeKeyView {
363	return YES;
364}
365
366- (BOOL)acceptsFirstResponder {
367	return YES;
368}
369
370- (void)cursorUpdate:(NSEvent *)event {
371	//setModeCursor(window, window->cursorMode);
372}
373
374static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
375	if (pressed) {
376		button_mask |= mask;
377	} else {
378		button_mask &= ~mask;
379	}
380
381	InputEvent ev;
382
383	ev.type = InputEvent::MOUSE_BUTTON;
384	ev.mouse_button.button_index = index;
385	ev.mouse_button.pressed = pressed;
386	ev.mouse_button.x = mouse_x;
387	ev.mouse_button.y = mouse_y;
388	ev.mouse_button.global_x = mouse_x;
389	ev.mouse_button.global_y = mouse_y;
390	ev.mouse_button.button_mask = button_mask;
391	if (index == BUTTON_LEFT && pressed) {
392		ev.mouse_button.doubleclick = [event clickCount] == 2;
393	}
394	ev.mouse_button.mod = translateFlags([event modifierFlags]);
395
396	OS_OSX::singleton->push_input(ev);
397}
398
399- (void)mouseDown:(NSEvent *)event {
400	if (([event modifierFlags] & NSControlKeyMask)) {
401		mouse_down_control = true;
402		_mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
403	} else {
404		mouse_down_control = false;
405		_mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, true);
406	}
407}
408
409- (void)mouseDragged:(NSEvent *)event {
410	[self mouseMoved:event];
411}
412
413- (void)mouseUp:(NSEvent *)event {
414	if (mouse_down_control) {
415		_mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
416	} else {
417		_mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, false);
418	}
419}
420
421- (void)mouseMoved:(NSEvent *)event {
422
423	InputEvent ev;
424	ev.type = InputEvent::MOUSE_MOTION;
425	ev.mouse_motion.button_mask = button_mask;
426	prev_mouse_x = mouse_x;
427	prev_mouse_y = mouse_y;
428	const NSRect contentRect = [OS_OSX::singleton->window_view frame];
429	const NSPoint p = [event locationInWindow];
430	mouse_x = p.x * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]);
431	mouse_y = (contentRect.size.height - p.y) * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]);
432	ev.mouse_motion.x = mouse_x;
433	ev.mouse_motion.y = mouse_y;
434	ev.mouse_motion.global_x = mouse_x;
435	ev.mouse_motion.global_y = mouse_y;
436	ev.mouse_motion.relative_x = [event deltaX] * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]);
437	ev.mouse_motion.relative_y = [event deltaY] * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]);
438	ev.mouse_motion.mod = translateFlags([event modifierFlags]);
439
440	OS_OSX::singleton->input->set_mouse_pos(Point2(mouse_x, mouse_y));
441	OS_OSX::singleton->push_input(ev);
442
443	/*
444	if (window->cursorMode == GLFW_CURSOR_DISABLED)
445		_glfwInputCursorMotion(window, [event deltaX], [event deltaY]);
446	else {
447		const NSRect contentRect = [window->ns.view frame];
448		const NSPoint p = [event locationInWindow];
449
450		_glfwInputCursorMotion(window, p.x, contentRect.size.height - p.y);
451	}
452*/
453}
454
455- (void)rightMouseDown:(NSEvent *)event {
456	_mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
457}
458
459- (void)rightMouseDragged:(NSEvent *)event {
460	[self mouseMoved:event];
461}
462
463- (void)rightMouseUp:(NSEvent *)event {
464	_mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
465}
466
467- (void)otherMouseDown:(NSEvent *)event {
468
469	if ((int)[event buttonNumber] != 2)
470		return;
471
472	_mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true);
473}
474
475- (void)otherMouseDragged:(NSEvent *)event {
476	[self mouseMoved:event];
477}
478
479- (void)otherMouseUp:(NSEvent *)event {
480
481	if ((int)[event buttonNumber] != 2)
482		return;
483
484	_mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false);
485}
486
487- (void)mouseExited:(NSEvent *)event {
488	if (!OS_OSX::singleton)
489		return;
490
491	if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
492		OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
493
494	//_glfwInputCursorEnter(window, GL_FALSE);
495}
496
497- (void)mouseEntered:(NSEvent *)event {
498	//_glfwInputCursorEnter(window, GL_TRUE);
499	if (!OS_OSX::singleton)
500		return;
501
502	if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
503		OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
504
505	if (OS_OSX::singleton->input) {
506		OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
507		OS_OSX::singleton->set_cursor_shape(OS::CURSOR_ARROW);
508	}
509}
510
511- (void)viewDidChangeBackingProperties {
512	/*
513	const NSRect contentRect = [window->ns.view frame];
514	const NSRect fbRect = convertRectToBacking(window, contentRect);
515
516	_glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
517*/
518}
519
520- (void)updateTrackingAreas {
521	if (trackingArea != nil) {
522		[self removeTrackingArea:trackingArea];
523		[trackingArea release];
524	}
525
526	NSTrackingAreaOptions options =
527			NSTrackingMouseEnteredAndExited |
528			NSTrackingActiveInKeyWindow |
529			NSTrackingCursorUpdate |
530			NSTrackingInVisibleRect;
531
532	trackingArea = [[NSTrackingArea alloc]
533			initWithRect:[self bounds]
534				 options:options
535				   owner:self
536				userInfo:nil];
537
538	[self addTrackingArea:trackingArea];
539	[super updateTrackingAreas];
540}
541
542// Translates a OS X keycode to a Godot keycode
543//
544static int translateKey(unsigned int key) {
545	// Keyboard symbol translation table
546	static const unsigned int table[128] = {
547		/* 00 */ KEY_A,
548		/* 01 */ KEY_S,
549		/* 02 */ KEY_D,
550		/* 03 */ KEY_F,
551		/* 04 */ KEY_H,
552		/* 05 */ KEY_G,
553		/* 06 */ KEY_Z,
554		/* 07 */ KEY_X,
555		/* 08 */ KEY_C,
556		/* 09 */ KEY_V,
557		/* 0a */ KEY_UNKNOWN,
558		/* 0b */ KEY_B,
559		/* 0c */ KEY_Q,
560		/* 0d */ KEY_W,
561		/* 0e */ KEY_E,
562		/* 0f */ KEY_R,
563		/* 10 */ KEY_Y,
564		/* 11 */ KEY_T,
565		/* 12 */ KEY_1,
566		/* 13 */ KEY_2,
567		/* 14 */ KEY_3,
568		/* 15 */ KEY_4,
569		/* 16 */ KEY_6,
570		/* 17 */ KEY_5,
571		/* 18 */ KEY_EQUAL,
572		/* 19 */ KEY_9,
573		/* 1a */ KEY_7,
574		/* 1b */ KEY_MINUS,
575		/* 1c */ KEY_8,
576		/* 1d */ KEY_0,
577		/* 1e */ KEY_BRACERIGHT,
578		/* 1f */ KEY_O,
579		/* 20 */ KEY_U,
580		/* 21 */ KEY_BRACELEFT,
581		/* 22 */ KEY_I,
582		/* 23 */ KEY_P,
583		/* 24 */ KEY_RETURN,
584		/* 25 */ KEY_L,
585		/* 26 */ KEY_J,
586		/* 27 */ KEY_APOSTROPHE,
587		/* 28 */ KEY_K,
588		/* 29 */ KEY_SEMICOLON,
589		/* 2a */ KEY_BACKSLASH,
590		/* 2b */ KEY_COMMA,
591		/* 2c */ KEY_SLASH,
592		/* 2d */ KEY_N,
593		/* 2e */ KEY_M,
594		/* 2f */ KEY_PERIOD,
595		/* 30 */ KEY_TAB,
596		/* 31 */ KEY_SPACE,
597		/* 32 */ KEY_QUOTELEFT,
598		/* 33 */ KEY_BACKSPACE,
599		/* 34 */ KEY_UNKNOWN,
600		/* 35 */ KEY_ESCAPE,
601		/* 36 */ KEY_META,
602		/* 37 */ KEY_META,
603		/* 38 */ KEY_SHIFT,
604		/* 39 */ KEY_CAPSLOCK,
605		/* 3a */ KEY_ALT,
606		/* 3b */ KEY_CONTROL,
607		/* 3c */ KEY_SHIFT,
608		/* 3d */ KEY_ALT,
609		/* 3e */ KEY_CONTROL,
610		/* 3f */ KEY_UNKNOWN, /* Function */
611		/* 40 */ KEY_UNKNOWN,
612		/* 41 */ KEY_KP_PERIOD,
613		/* 42 */ KEY_UNKNOWN,
614		/* 43 */ KEY_KP_MULTIPLY,
615		/* 44 */ KEY_UNKNOWN,
616		/* 45 */ KEY_KP_ADD,
617		/* 46 */ KEY_UNKNOWN,
618		/* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */
619		/* 48 */ KEY_UNKNOWN, /* VolumeUp */
620		/* 49 */ KEY_UNKNOWN, /* VolumeDown */
621		/* 4a */ KEY_UNKNOWN, /* Mute */
622		/* 4b */ KEY_KP_DIVIDE,
623		/* 4c */ KEY_ENTER,
624		/* 4d */ KEY_UNKNOWN,
625		/* 4e */ KEY_KP_SUBTRACT,
626		/* 4f */ KEY_UNKNOWN,
627		/* 50 */ KEY_UNKNOWN,
628		/* 51 */ KEY_EQUAL, //wtf equal?
629		/* 52 */ KEY_KP_0,
630		/* 53 */ KEY_KP_1,
631		/* 54 */ KEY_KP_2,
632		/* 55 */ KEY_KP_3,
633		/* 56 */ KEY_KP_4,
634		/* 57 */ KEY_KP_5,
635		/* 58 */ KEY_KP_6,
636		/* 59 */ KEY_KP_7,
637		/* 5a */ KEY_UNKNOWN,
638		/* 5b */ KEY_KP_8,
639		/* 5c */ KEY_KP_9,
640		/* 5d */ KEY_UNKNOWN,
641		/* 5e */ KEY_UNKNOWN,
642		/* 5f */ KEY_UNKNOWN,
643		/* 60 */ KEY_F5,
644		/* 61 */ KEY_F6,
645		/* 62 */ KEY_F7,
646		/* 63 */ KEY_F3,
647		/* 64 */ KEY_F8,
648		/* 65 */ KEY_F9,
649		/* 66 */ KEY_UNKNOWN,
650		/* 67 */ KEY_F11,
651		/* 68 */ KEY_UNKNOWN,
652		/* 69 */ KEY_F13,
653		/* 6a */ KEY_F16,
654		/* 6b */ KEY_F14,
655		/* 6c */ KEY_UNKNOWN,
656		/* 6d */ KEY_F10,
657		/* 6e */ KEY_UNKNOWN,
658		/* 6f */ KEY_F12,
659		/* 70 */ KEY_UNKNOWN,
660		/* 71 */ KEY_F15,
661		/* 72 */ KEY_INSERT, /* Really Help... */
662		/* 73 */ KEY_HOME,
663		/* 74 */ KEY_PAGEUP,
664		/* 75 */ KEY_DELETE,
665		/* 76 */ KEY_F4,
666		/* 77 */ KEY_END,
667		/* 78 */ KEY_F2,
668		/* 79 */ KEY_PAGEDOWN,
669		/* 7a */ KEY_F1,
670		/* 7b */ KEY_LEFT,
671		/* 7c */ KEY_RIGHT,
672		/* 7d */ KEY_DOWN,
673		/* 7e */ KEY_UP,
674		/* 7f */ KEY_UNKNOWN,
675	};
676
677	if (key >= 128)
678		return KEY_UNKNOWN;
679
680	return table[key];
681}
682
683- (void)keyDown:(NSEvent *)event {
684	InputEvent ev;
685	ev.type = InputEvent::KEY;
686	ev.key.pressed = true;
687	ev.key.mod = translateFlags([event modifierFlags]);
688	ev.key.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode]));
689	ev.key.echo = [event isARepeat];
690
691	NSString *characters = [event characters];
692	NSUInteger i, length = [characters length];
693
694	if (length > 0 && keycode_has_unicode(ev.key.scancode)) {
695		for (i = 0; i < length; i++) {
696			ev.key.unicode = [characters characterAtIndex:i];
697			OS_OSX::singleton->push_input(ev);
698			ev.key.scancode = 0;
699		}
700	} else {
701		OS_OSX::singleton->push_input(ev);
702	}
703}
704
705- (void)flagsChanged:(NSEvent *)event {
706	InputEvent ev;
707	int key = [event keyCode];
708	int mod = [event modifierFlags];
709
710	ev.type = InputEvent::KEY;
711
712	if (key == 0x36 || key == 0x37) {
713		if (mod & NSCommandKeyMask) {
714			mod &= ~NSCommandKeyMask;
715			ev.key.pressed = true;
716		} else {
717			ev.key.pressed = false;
718		}
719	} else if (key == 0x38 || key == 0x3c) {
720		if (mod & NSShiftKeyMask) {
721			mod &= ~NSShiftKeyMask;
722			ev.key.pressed = true;
723		} else {
724			ev.key.pressed = false;
725		}
726	} else if (key == 0x3a || key == 0x3d) {
727		if (mod & NSAlternateKeyMask) {
728			mod &= ~NSAlternateKeyMask;
729			ev.key.pressed = true;
730		} else {
731			ev.key.pressed = false;
732		}
733	} else if (key == 0x3b || key == 0x3e) {
734		if (mod & NSControlKeyMask) {
735			mod &= ~NSControlKeyMask;
736			ev.key.pressed = true;
737		} else {
738			ev.key.pressed = false;
739		}
740	} else {
741		return;
742	}
743
744	ev.key.mod = translateFlags(mod);
745	ev.key.scancode = latin_keyboard_keycode_convert(translateKey(key));
746
747	OS_OSX::singleton->push_input(ev);
748}
749
750- (void)keyUp:(NSEvent *)event {
751
752	InputEvent ev;
753	ev.type = InputEvent::KEY;
754	ev.key.pressed = false;
755	ev.key.mod = translateFlags([event modifierFlags]);
756	ev.key.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode]));
757	OS_OSX::singleton->push_input(ev);
758
759	/*
760	const int key = translateKey([event keyCode]);
761	const int mods = translateFlags([event modifierFlags]);
762	_glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods);
763*/
764}
765
766inline void sendScrollEvent(int button, double factor) {
767	InputEvent ev;
768	ev.type = InputEvent::MOUSE_BUTTON;
769	ev.mouse_button.button_index = button;
770	ev.mouse_button.factor = factor;
771	ev.mouse_button.pressed = true;
772	ev.mouse_button.x = mouse_x;
773	ev.mouse_button.y = mouse_y;
774	ev.mouse_button.global_x = mouse_x;
775	ev.mouse_button.global_y = mouse_y;
776	ev.mouse_button.button_mask = button_mask;
777	OS_OSX::singleton->push_input(ev);
778	ev.mouse_button.pressed = false;
779	OS_OSX::singleton->push_input(ev);
780}
781
782- (void)scrollWheel:(NSEvent *)event {
783
784	double deltaX, deltaY;
785
786#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
787	if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) {
788		deltaX = [event scrollingDeltaX];
789		deltaY = [event scrollingDeltaY];
790
791		if ([event hasPreciseScrollingDeltas]) {
792			deltaX *= 0.03;
793			deltaY *= 0.03;
794		}
795	} else
796#endif // MAC_OS_X_VERSION_MAX_ALLOWED
797	{
798		deltaX = [event deltaX];
799		deltaY = [event deltaY];
800	}
801
802	if (fabs(deltaX)) {
803		sendScrollEvent(0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3));
804	}
805	if (fabs(deltaY)) {
806		sendScrollEvent(0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3));
807	}
808}
809
810@end
811
812@interface GodotWindow : NSWindow {
813}
814@end
815
816@implementation GodotWindow
817
818- (BOOL)canBecomeKeyWindow {
819	// Required for NSBorderlessWindowMask windows
820	return YES;
821}
822
823@end
824
825int OS_OSX::get_video_driver_count() const {
826	return 1;
827}
828
829const char *OS_OSX::get_video_driver_name(int p_driver) const {
830	return "GLES2";
831}
832
833OS::VideoMode OS_OSX::get_default_video_mode() const {
834
835	VideoMode vm;
836	vm.width = 1024;
837	vm.height = 600;
838	vm.fullscreen = false;
839	vm.resizable = true;
840	return vm;
841}
842
843void OS_OSX::initialize_core() {
844
845	crash_handler.initialize();
846
847	OS_Unix::initialize_core();
848
849	DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
850	DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA);
851	DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
852
853	SemaphoreOSX::make_default();
854}
855
856static bool keyboard_layout_dirty = true;
857static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
858	keyboard_layout_dirty = true;
859}
860
861static bool displays_arrangement_dirty = true;
862static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
863	displays_arrangement_dirty = true;
864}
865
866void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
867
868	/*** OSX INITIALIZATION ***/
869	/*** OSX INITIALIZATION ***/
870	/*** OSX INITIALIZATION ***/
871
872	keyboard_layout_dirty = true;
873	displays_arrangement_dirty = true;
874
875	// Register to be notified on keyboard layout changes
876	CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
877			NULL, keyboard_layout_changed,
878			kTISNotifySelectedKeyboardInputSourceChanged, NULL,
879			CFNotificationSuspensionBehaviorDeliverImmediately);
880
881	// Register to be notified on displays arrangement changes
882	CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL);
883
884	if (is_hidpi_allowed() && [[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)]) {
885		for (NSScreen *screen in [NSScreen screens]) {
886			float s = [screen backingScaleFactor];
887			if (s > display_scale) {
888				display_scale = s;
889			}
890		}
891	}
892
893	window_delegate = [[GodotWindowDelegate alloc] init];
894
895	// Don't use accumulation buffer support; it's not accelerated
896	// Aux buffers probably aren't accelerated either
897
898	unsigned int styleMask;
899
900	if (p_desired.borderless_window) {
901		styleMask = NSWindowStyleMaskBorderless;
902	} else {
903		styleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | (p_desired.resizable ? NSResizableWindowMask : 0);
904	}
905
906	window_object = [[GodotWindow alloc]
907			initWithContentRect:NSMakeRect(0, 0, p_desired.width, p_desired.height)
908					  styleMask:styleMask
909						backing:NSBackingStoreBuffered
910						  defer:NO];
911
912	ERR_FAIL_COND(window_object == nil);
913
914	window_view = [[GodotContentView alloc] init];
915
916	window_size.width = p_desired.width * display_scale;
917	window_size.height = p_desired.height * display_scale;
918
919#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
920	if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6 && display_scale > 1) {
921		[window_view setWantsBestResolutionOpenGLSurface:YES];
922		//if (current_videomode.resizable)
923		[window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
924	}
925#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
926
927	//[window_object setTitle:[NSString stringWithUTF8String:"GodotEnginies"]];
928	[window_object setContentView:window_view];
929	[window_object setDelegate:window_delegate];
930	[window_object setAcceptsMouseMovedEvents:YES];
931	[window_object center];
932
933#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
934	if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6)
935		[window_object setRestorable:NO];
936#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
937
938	unsigned int attributeCount = 0;
939
940	// OS X needs non-zero color size, so set resonable values
941	int colorBits = 32;
942
943// Fail if a robustness strategy was requested
944#define ADD_ATTR(x) \
945	{ attributes[attributeCount++] = x; }
946#define ADD_ATTR2(x, y) \
947	{                   \
948		ADD_ATTR(x);    \
949		ADD_ATTR(y);    \
950	}
951
952	// Arbitrary array size here
953	NSOpenGLPixelFormatAttribute attributes[40];
954
955	ADD_ATTR(NSOpenGLPFADoubleBuffer);
956	ADD_ATTR(NSOpenGLPFAClosestPolicy);
957
958#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
959	if (false /* use gl3*/)
960		ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
961#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
962
963	ADD_ATTR2(NSOpenGLPFAColorSize, colorBits);
964
965	/*
966	if (fbconfig->alphaBits > 0)
967		ADD_ATTR2(NSOpenGLPFAAlphaSize, fbconfig->alphaBits);
968*/
969
970	ADD_ATTR2(NSOpenGLPFADepthSize, 24);
971
972	ADD_ATTR2(NSOpenGLPFAStencilSize, 8);
973
974	/*
975	if (fbconfig->stereo)
976		ADD_ATTR(NSOpenGLPFAStereo);
977*/
978
979	/*
980	if (fbconfig->samples > 0) {
981		 ADD_ATTR2(NSOpenGLPFASampleBuffers, 1);
982		 ADD_ATTR2(NSOpenGLPFASamples, fbconfig->samples);
983	}
984*/
985
986	// NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB
987	//       frambuffer, so there's no need (and no way) to request it
988
989	ADD_ATTR(0);
990
991#undef ADD_ATTR
992#undef ADD_ATTR2
993
994	pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
995	ERR_FAIL_COND(pixelFormat == nil);
996
997	context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
998
999	ERR_FAIL_COND(context == nil);
1000
1001	[context setView:window_view];
1002
1003	[context makeCurrentContext];
1004
1005	[NSApp activateIgnoringOtherApps:YES];
1006
1007	[window_object makeKeyAndOrderFront:nil];
1008
1009	if (p_desired.fullscreen)
1010		zoomed = true;
1011
1012	/*** END OSX INITIALIZATION ***/
1013	/*** END OSX INITIALIZATION ***/
1014	/*** END OSX INITIALIZATION ***/
1015
1016	bool use_gl2 = p_video_driver != 1;
1017
1018	AudioDriverManagerSW::add_driver(&audio_driver_osx);
1019
1020	rasterizer = instance_RasterizerGLES2();
1021
1022	visual_server = memnew(VisualServerRaster(rasterizer));
1023
1024	if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
1025		visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
1026	}
1027
1028	visual_server->init();
1029
1030	AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton();
1031
1032	if (AudioDriverManagerSW::get_driver(p_audio_driver)->init() != OK) {
1033
1034		ERR_PRINT("Initializing audio failed.");
1035	}
1036
1037	sample_manager = memnew(SampleManagerMallocSW);
1038	audio_server = memnew(AudioServerSW(sample_manager));
1039
1040	audio_server->set_mixer_params(AudioMixerSW::INTERPOLATION_LINEAR, false);
1041	audio_server->init();
1042
1043	spatial_sound_server = memnew(SpatialSoundServerSW);
1044	spatial_sound_server->init();
1045
1046	spatial_sound_2d_server = memnew(SpatialSound2DServerSW);
1047	spatial_sound_2d_server->init();
1048
1049	physics_server = memnew(PhysicsServerSW);
1050	physics_server->init();
1051	//physics_2d_server = memnew( Physics2DServerSW );
1052	physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>();
1053	physics_2d_server->init();
1054
1055	input = memnew(InputDefault);
1056	joystick_osx = memnew(JoystickOSX);
1057
1058	_ensure_data_dir();
1059
1060	restore_rect = Rect2(get_window_position(), get_window_size());
1061}
1062
1063void OS_OSX::finalize() {
1064
1065	CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
1066	CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
1067
1068	delete_main_loop();
1069
1070	spatial_sound_server->finish();
1071	memdelete(spatial_sound_server);
1072	spatial_sound_2d_server->finish();
1073	memdelete(spatial_sound_2d_server);
1074
1075	memdelete(joystick_osx);
1076	memdelete(input);
1077
1078	memdelete(sample_manager);
1079
1080	audio_server->finish();
1081	memdelete(audio_server);
1082
1083	visual_server->finish();
1084	memdelete(visual_server);
1085	memdelete(rasterizer);
1086
1087	physics_server->finish();
1088	memdelete(physics_server);
1089
1090	physics_2d_server->finish();
1091	memdelete(physics_2d_server);
1092}
1093
1094void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
1095
1096	main_loop = p_main_loop;
1097	input->set_main_loop(p_main_loop);
1098}
1099
1100void OS_OSX::delete_main_loop() {
1101
1102	if (!main_loop)
1103		return;
1104
1105	memdelete(main_loop);
1106	main_loop = NULL;
1107}
1108
1109String OS_OSX::get_name() {
1110
1111	return "OSX";
1112}
1113
1114void OS_OSX::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
1115
1116#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
1117	if (!_print_error_enabled)
1118		return;
1119
1120	const char *err_details;
1121	if (p_rationale && p_rationale[0])
1122		err_details = p_rationale;
1123	else
1124		err_details = p_code;
1125
1126	switch (p_type) {
1127		case ERR_ERROR:
1128			os_log_error(OS_LOG_DEFAULT, "ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line);
1129			print("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
1130			print("\E[0;31m   At: %s:%i.\E[0m\n", p_file, p_line);
1131			break;
1132		case ERR_WARNING:
1133			os_log_info(OS_LOG_DEFAULT, "WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line);
1134			print("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function, err_details);
1135			print("\E[0;33m   At: %s:%i.\E[0m\n", p_file, p_line);
1136			break;
1137		case ERR_SCRIPT:
1138			os_log_error(OS_LOG_DEFAULT, "SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line);
1139			print("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
1140			print("\E[0;35m   At: %s:%i.\E[0m\n", p_file, p_line);
1141			break;
1142	}
1143#else
1144	OS_Unix::print_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
1145#endif
1146}
1147
1148void OS_OSX::alert(const String &p_alert, const String &p_title) {
1149	// Set OS X-compliant variables
1150	NSAlert *window = [[NSAlert alloc] init];
1151	NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
1152	NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
1153
1154	[window addButtonWithTitle:@"OK"];
1155	[window setMessageText:ns_title];
1156	[window setInformativeText:ns_alert];
1157	[window setAlertStyle:NSWarningAlertStyle];
1158
1159	// Display it, then release
1160	[window runModal];
1161	[window release];
1162}
1163
1164void OS_OSX::set_cursor_shape(CursorShape p_shape) {
1165
1166	if (cursor_shape == p_shape)
1167		return;
1168
1169	if (mouse_mode != MOUSE_MODE_VISIBLE) {
1170		cursor_shape = p_shape;
1171		return;
1172	}
1173
1174	if (cursors[p_shape] != NULL) {
1175		[cursors[p_shape] set];
1176	} else {
1177		switch (p_shape) {
1178			case CURSOR_ARROW: [[NSCursor arrowCursor] set]; break;
1179			case CURSOR_IBEAM: [[NSCursor IBeamCursor] set]; break;
1180			case CURSOR_POINTING_HAND: [[NSCursor pointingHandCursor] set]; break;
1181			case CURSOR_CROSS: [[NSCursor crosshairCursor] set]; break;
1182			case CURSOR_WAIT: [[NSCursor arrowCursor] set]; break;
1183			case CURSOR_BUSY: [[NSCursor arrowCursor] set]; break;
1184			case CURSOR_DRAG: [[NSCursor closedHandCursor] set]; break;
1185			case CURSOR_CAN_DROP: [[NSCursor openHandCursor] set]; break;
1186			case CURSOR_FORBIDDEN: [[NSCursor arrowCursor] set]; break;
1187			case CURSOR_VSIZE: [[NSCursor resizeUpDownCursor] set]; break;
1188			case CURSOR_HSIZE: [[NSCursor resizeLeftRightCursor] set]; break;
1189			case CURSOR_BDIAGSIZE: [[NSCursor arrowCursor] set]; break;
1190			case CURSOR_FDIAGSIZE: [[NSCursor arrowCursor] set]; break;
1191			case CURSOR_MOVE: [[NSCursor arrowCursor] set]; break;
1192			case CURSOR_VSPLIT: [[NSCursor resizeUpDownCursor] set]; break;
1193			case CURSOR_HSPLIT: [[NSCursor resizeLeftRightCursor] set]; break;
1194			case CURSOR_HELP: [[NSCursor arrowCursor] set]; break;
1195			default: {};
1196		}
1197	}
1198
1199	cursor_shape = p_shape;
1200}
1201
1202void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
1203	if (p_cursor.is_valid()) {
1204		Ref<ImageTexture> texture = p_cursor;
1205		Ref<AtlasTexture> atlas_texture = p_cursor;
1206		Size2 texture_size;
1207		Rect2 atlas_rect;
1208
1209		if (!texture.is_valid() && atlas_texture.is_valid()) {
1210			texture = atlas_texture->get_atlas();
1211
1212			atlas_rect.size.width = texture->get_width();
1213			atlas_rect.size.height = texture->get_height();
1214			atlas_rect.pos.x = atlas_texture->get_region().pos.x;
1215			atlas_rect.pos.y = atlas_texture->get_region().pos.y;
1216
1217			texture_size.width = atlas_texture->get_region().size.x;
1218			texture_size.height = atlas_texture->get_region().size.y;
1219		} else if (texture.is_valid()) {
1220			texture_size.width = texture->get_width();
1221			texture_size.height = texture->get_height();
1222		}
1223
1224		ERR_FAIL_COND(!texture.is_valid());
1225		ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
1226
1227		Image image = texture->get_data();
1228
1229		NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
1230				initWithBitmapDataPlanes:NULL
1231							  pixelsWide:int(texture_size.width)
1232							  pixelsHigh:int(texture_size.height)
1233						   bitsPerSample:8
1234						 samplesPerPixel:4
1235								hasAlpha:YES
1236								isPlanar:NO
1237						  colorSpaceName:NSDeviceRGBColorSpace
1238							 bytesPerRow:int(texture_size.width) * 4
1239							bitsPerPixel:32];
1240		ERR_FAIL_COND(imgrep == nil);
1241		uint8_t *pixels = [imgrep bitmapData];
1242
1243		int len = int(texture_size.width * texture_size.height);
1244		DVector<uint8_t> data = image.get_data();
1245		DVector<uint8_t>::Read r = data.read();
1246
1247		/* Premultiply the alpha channel */
1248		for (int i = 0; i < len; i++) {
1249			int row_index = floor(i / texture_size.width) + atlas_rect.pos.y;
1250			int column_index = (i % int(texture_size.width)) + atlas_rect.pos.x;
1251
1252			if (atlas_texture.is_valid()) {
1253				column_index = MIN(column_index, atlas_rect.size.width - 1);
1254				row_index = MIN(row_index, atlas_rect.size.height - 1);
1255			}
1256
1257			uint32_t color = image.get_pixel(column_index, row_index).to_ARGB32();
1258
1259			uint8_t alpha = (color >> 24) & 0xFF;
1260			pixels[i * 4 + 0] = ((color >> 16) & 0xFF) * alpha / 255;
1261			pixels[i * 4 + 1] = ((color >> 8) & 0xFF) * alpha / 255;
1262			pixels[i * 4 + 2] = ((color)&0xFF) * alpha / 255;
1263			pixels[i * 4 + 3] = alpha;
1264		}
1265
1266		NSImage *nsimage = [[NSImage alloc] initWithSize:NSMakeSize(texture_size.width, texture_size.height)];
1267		[nsimage addRepresentation:imgrep];
1268
1269		[cursors[p_shape] release];
1270		NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)];
1271
1272		cursors[p_shape] = cursor;
1273
1274		if (p_shape == CURSOR_ARROW) {
1275			if (mouse_mode == MOUSE_MODE_VISIBLE) {
1276				[cursor set];
1277			}
1278		}
1279
1280		[imgrep release];
1281		[nsimage release];
1282	}
1283}
1284
1285void OS_OSX::set_mouse_show(bool p_show) {
1286}
1287
1288void OS_OSX::set_mouse_grab(bool p_grab) {
1289}
1290
1291bool OS_OSX::is_mouse_grab_enabled() const {
1292
1293	return mouse_grab;
1294}
1295
1296void OS_OSX::warp_mouse_pos(const Point2 &p_to) {
1297
1298	//copied from windows impl with osx native calls
1299	if (mouse_mode == MOUSE_MODE_CAPTURED) {
1300		mouse_x = p_to.x;
1301		mouse_y = p_to.y;
1302	} else {
1303		//set OS position
1304
1305		//local point in window coords
1306		const NSRect contentRect = [window_view frame];
1307		NSRect pointInWindowRect = NSMakeRect(p_to.x / display_scale, contentRect.size.height - (p_to.y / display_scale) - 1, 0, 0);
1308		NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin;
1309
1310		//point in scren coords
1311		CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
1312
1313		//do the warping
1314		CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
1315		CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
1316		CGAssociateMouseAndMouseCursorPosition(false);
1317		CGWarpMouseCursorPosition(lMouseWarpPos);
1318		CGAssociateMouseAndMouseCursorPosition(true);
1319	}
1320}
1321
1322Point2 OS_OSX::get_mouse_pos() const {
1323
1324	return Vector2(mouse_x, mouse_y);
1325}
1326
1327int OS_OSX::get_mouse_button_state() const {
1328	return button_mask;
1329}
1330
1331void OS_OSX::set_window_title(const String &p_title) {
1332	title = p_title;
1333
1334	[window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
1335}
1336
1337void OS_OSX::set_icon(const Image &p_icon) {
1338
1339	Image img = p_icon;
1340	img.convert(Image::FORMAT_RGBA);
1341	NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc]
1342			initWithBitmapDataPlanes:NULL
1343						  pixelsWide:p_icon.get_width()
1344						  pixelsHigh:p_icon.get_height()
1345					   bitsPerSample:8
1346					 samplesPerPixel:4
1347							hasAlpha:YES
1348							isPlanar:NO
1349					  colorSpaceName:NSDeviceRGBColorSpace
1350						 bytesPerRow:p_icon.get_width() * 4
1351						bitsPerPixel:32] autorelease];
1352	ERR_FAIL_COND(imgrep == nil);
1353	uint8_t *pixels = [imgrep bitmapData];
1354
1355	int len = img.get_width() * img.get_height();
1356	DVector<uint8_t> data = img.get_data();
1357	DVector<uint8_t>::Read r = data.read();
1358
1359	/* Premultiply the alpha channel */
1360	for (int i = 0; i < len; i++) {
1361		uint8_t alpha = r[i * 4 + 3];
1362		pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
1363		pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
1364		pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
1365		pixels[i * 4 + 3] = alpha;
1366	}
1367
1368	NSImage *nsimg = [[[NSImage alloc] initWithSize:NSMakeSize(img.get_width(), img.get_height())] autorelease];
1369	ERR_FAIL_COND(nsimg == nil);
1370	[nsimg addRepresentation:imgrep];
1371
1372	[NSApp setApplicationIconImage:nsimg];
1373}
1374
1375MainLoop *OS_OSX::get_main_loop() const {
1376
1377	return main_loop;
1378}
1379
1380bool OS_OSX::can_draw() const {
1381
1382	return true;
1383}
1384
1385void OS_OSX::set_clipboard(const String &p_text) {
1386
1387	NSArray *types = [NSArray arrayWithObjects:NSStringPboardType, nil];
1388
1389	NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
1390	[pasteboard declareTypes:types owner:nil];
1391	[pasteboard setString:[NSString stringWithUTF8String:p_text.utf8().get_data()]
1392				  forType:NSStringPboardType];
1393}
1394
1395String OS_OSX::get_clipboard() const {
1396
1397	NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
1398
1399	if (![[pasteboard types] containsObject:NSStringPboardType]) {
1400		return "";
1401	}
1402
1403	NSString *object = [pasteboard stringForType:NSStringPboardType];
1404	if (!object) {
1405		return "";
1406	}
1407
1408	char *utfs = strdup([object UTF8String]);
1409	String ret;
1410	ret.parse_utf8(utfs);
1411	free(utfs);
1412
1413	return ret;
1414}
1415
1416void OS_OSX::release_rendering_thread() {
1417
1418	[NSOpenGLContext clearCurrentContext];
1419}
1420
1421void OS_OSX::make_rendering_thread() {
1422
1423	[context makeCurrentContext];
1424}
1425
1426Error OS_OSX::shell_open(String p_uri) {
1427
1428	[[NSWorkspace sharedWorkspace] openURL:[[NSURL alloc] initWithString:[NSString stringWithUTF8String:p_uri.utf8().get_data()]]];
1429	return OK;
1430}
1431
1432String OS_OSX::get_locale() const {
1433	NSString *locale_code = [[NSLocale currentLocale] localeIdentifier];
1434	return [locale_code UTF8String];
1435}
1436
1437void OS_OSX::swap_buffers() {
1438
1439	[context flushBuffer];
1440}
1441
1442void OS_OSX::wm_minimized(bool p_minimized) {
1443
1444	minimized = p_minimized;
1445};
1446
1447void OS_OSX::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
1448}
1449
1450OS::VideoMode OS_OSX::get_video_mode(int p_screen) const {
1451
1452	VideoMode vm;
1453	vm.width = window_size.width;
1454	vm.height = window_size.height;
1455
1456	return vm;
1457}
1458
1459void OS_OSX::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
1460}
1461
1462int OS_OSX::get_screen_count() const {
1463	NSArray *screenArray = [NSScreen screens];
1464	return [screenArray count];
1465};
1466
1467// Returns the native top-left screen coordinate of the smallest rectangle
1468// that encompasses all screens. Needed in get_screen_position(),
1469// get_window_position, and set_window_position()
1470// to convert between OS X native screen coordinates and the ones expected by Godot
1471Point2 OS_OSX::get_screens_origin() const {
1472	static Point2 origin;
1473
1474	if (displays_arrangement_dirty) {
1475		origin = Point2();
1476
1477		for (int i = 0; i < get_screen_count(); i++) {
1478			Point2 position = get_native_screen_position(i);
1479			if (position.x < origin.x) {
1480				origin.x = position.x;
1481			}
1482			if (position.y > origin.y) {
1483				origin.y = position.y;
1484			}
1485		}
1486
1487		displays_arrangement_dirty = false;
1488	}
1489
1490	return origin;
1491}
1492
1493static int get_screen_index(NSScreen *screen) {
1494	const NSUInteger index = [[NSScreen screens] indexOfObject:screen];
1495	return index == NSNotFound ? 0 : index;
1496}
1497
1498int OS_OSX::get_current_screen() const {
1499	if (window_object) {
1500		return get_screen_index([window_object screen]);
1501	} else {
1502		return get_screen_index([NSScreen mainScreen]);
1503	}
1504};
1505
1506void OS_OSX::set_current_screen(int p_screen) {
1507	Vector2 wpos = get_window_position() - get_screen_position(get_current_screen());
1508	set_window_position(wpos + get_screen_position(p_screen));
1509};
1510
1511Point2 OS_OSX::get_native_screen_position(int p_screen) const {
1512	NSArray *screenArray = [NSScreen screens];
1513	if (p_screen < [screenArray count]) {
1514		float displayScale = 1.0;
1515
1516		if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) {
1517			displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
1518		}
1519
1520		NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
1521		// Return the top-left corner of the screen, for OS X the y starts at the bottom
1522		return Point2(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * displayScale;
1523	}
1524
1525	return Point2();
1526}
1527
1528Point2 OS_OSX::get_screen_position(int p_screen) const {
1529	Point2 position = get_native_screen_position(p_screen) - get_screens_origin();
1530	// OS X native y-coordinate relative to get_screens_origin() is negative,
1531	// Godot expects a positive value
1532	position.y *= -1;
1533	return position;
1534}
1535
1536int OS_OSX::get_screen_dpi(int p_screen) const {
1537	NSArray *screenArray = [NSScreen screens];
1538	if (p_screen < [screenArray count]) {
1539		float displayScale = 1.0;
1540
1541		if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) {
1542			displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
1543		}
1544
1545		NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
1546		NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
1547		CGSize displayPhysicalSize = CGDisplayScreenSize(
1548				[[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
1549
1550		return (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale;
1551	}
1552
1553	return 72;
1554}
1555
1556Size2 OS_OSX::get_screen_size(int p_screen) const {
1557	NSArray *screenArray = [NSScreen screens];
1558	if (p_screen < [screenArray count]) {
1559		float displayScale = 1.0;
1560
1561		if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) {
1562			displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
1563		}
1564
1565		// Note: Use frame to get the whole screen size
1566		NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
1567		return Size2(nsrect.size.width, nsrect.size.height) * displayScale;
1568	}
1569
1570	return Size2();
1571}
1572
1573Point2 OS_OSX::get_native_window_position() const {
1574	NSRect nsrect = [window_object frame];
1575
1576	Point2 pos;
1577
1578	// Return the position of the top-left corner, for OS X the y starts at the bottom
1579	pos.x = nsrect.origin.x * display_scale;
1580	pos.y = (nsrect.origin.y + nsrect.size.height) * display_scale;
1581
1582	return pos;
1583};
1584
1585Point2 OS_OSX::get_window_position() const {
1586	Point2 position = get_native_window_position() - get_screens_origin();
1587	// OS X native y-coordinate relative to get_screens_origin() is negative,
1588	// Godot expects a positive value
1589	position.y *= -1;
1590	return position;
1591}
1592
1593void OS_OSX::_update_window() {
1594	bool borderless_full = false;
1595
1596	if (get_borderless_window()) {
1597		NSRect frameRect = [window_object frame];
1598		NSRect screenRect = [[window_object screen] frame];
1599
1600		// Check if our window covers up the screen
1601		if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y &&
1602				frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) {
1603			borderless_full = true;
1604		}
1605	}
1606
1607	if (borderless_full) {
1608		// If the window covers up the screen set the level to above the main menu and hide on deactivate
1609		[window_object setLevel:NSMainMenuWindowLevel + 1];
1610		[window_object setHidesOnDeactivate:YES];
1611	} else {
1612		// Reset these when our window is not a borderless window that covers up the screen
1613		[window_object setLevel:NSNormalWindowLevel];
1614		[window_object setHidesOnDeactivate:NO];
1615	}
1616}
1617
1618void OS_OSX::set_native_window_position(const Point2 &p_position) {
1619
1620	NSPoint pos;
1621
1622	pos.x = p_position.x / display_scale;
1623	pos.y = p_position.y / display_scale;
1624
1625	[window_object setFrameTopLeftPoint:pos];
1626
1627	_update_window();
1628};
1629
1630void OS_OSX::set_window_position(const Point2 &p_position) {
1631	Point2 position = p_position;
1632	// OS X native y-coordinate relative to get_screens_origin() is negative,
1633	// Godot passes a positive value
1634	position.y *= -1;
1635	set_native_window_position(get_screens_origin() + position);
1636};
1637
1638Size2 OS_OSX::get_window_size() const {
1639
1640	return window_size;
1641};
1642
1643Size2 OS_OSX::get_real_window_size() const {
1644
1645	NSRect frame = [window_object frame];
1646	return Size2(frame.size.width, frame.size.height);
1647}
1648
1649void OS_OSX::set_window_size(const Size2 p_size) {
1650
1651	Size2 size = p_size;
1652
1653	if (get_borderless_window() == false) {
1654		// NSRect used by setFrame includes the title bar, so add it to our size.y
1655		CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
1656		if (menuBarHeight != 0.f) {
1657			size.y += menuBarHeight;
1658#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101104
1659		} else {
1660			size.y += [[NSStatusBar systemStatusBar] thickness];
1661#endif
1662		}
1663	}
1664
1665	NSRect frame = [window_object frame];
1666	[window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, size.x, size.y) display:YES];
1667
1668	_update_window();
1669};
1670
1671void OS_OSX::set_window_fullscreen(bool p_enabled) {
1672
1673	if (zoomed != p_enabled) {
1674#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
1675		[window_object toggleFullScreen:nil];
1676#else
1677		[window_object performZoom:nil];
1678#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
1679	}
1680	zoomed = p_enabled;
1681};
1682
1683bool OS_OSX::is_window_fullscreen() const {
1684
1685#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
1686	if ([window_object respondsToSelector:@selector(isZoomed)])
1687		return [window_object isZoomed];
1688#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
1689
1690	return zoomed;
1691};
1692
1693void OS_OSX::set_window_resizable(bool p_enabled) {
1694
1695	if (p_enabled)
1696		[window_object setStyleMask:[window_object styleMask] | NSResizableWindowMask];
1697	else
1698		[window_object setStyleMask:[window_object styleMask] & ~NSResizableWindowMask];
1699};
1700
1701bool OS_OSX::is_window_resizable() const {
1702
1703	return [window_object styleMask] & NSResizableWindowMask;
1704};
1705
1706void OS_OSX::set_window_minimized(bool p_enabled) {
1707
1708	if (p_enabled)
1709		[window_object performMiniaturize:nil];
1710	else
1711		[window_object deminiaturize:nil];
1712};
1713
1714bool OS_OSX::is_window_minimized() const {
1715
1716	if ([window_object respondsToSelector:@selector(isMiniaturized)])
1717		return [window_object isMiniaturized];
1718
1719	return minimized;
1720};
1721
1722void OS_OSX::set_window_maximized(bool p_enabled) {
1723
1724	if (p_enabled) {
1725		restore_rect = Rect2(get_window_position(), get_window_size());
1726		[window_object setFrame:[[[NSScreen screens] objectAtIndex:get_current_screen()] visibleFrame] display:YES];
1727	} else {
1728		set_window_size(restore_rect.size);
1729		set_window_position(restore_rect.pos);
1730	};
1731	maximized = p_enabled;
1732};
1733
1734bool OS_OSX::is_window_maximized() const {
1735
1736	// don't know
1737	return maximized;
1738};
1739
1740void OS_OSX::move_window_to_foreground() {
1741
1742	[window_object orderFrontRegardless];
1743}
1744
1745void OS_OSX::set_window_always_on_top(bool p_enabled) {
1746	if (is_window_always_on_top() == p_enabled)
1747		return;
1748
1749	if (p_enabled)
1750		[window_object setLevel:NSFloatingWindowLevel];
1751	else
1752		[window_object setLevel:NSNormalWindowLevel];
1753}
1754
1755bool OS_OSX::is_window_always_on_top() const {
1756	return [window_object level] == NSFloatingWindowLevel;
1757}
1758
1759void OS_OSX::request_attention() {
1760
1761	[NSApp requestUserAttention:NSCriticalRequest];
1762}
1763
1764void OS_OSX::set_borderless_window(int p_borderless) {
1765
1766	// OrderOut prevents a lose focus bug with the window
1767	[window_object orderOut:nil];
1768
1769	if (p_borderless) {
1770		[window_object setStyleMask:NSWindowStyleMaskBorderless];
1771	} else {
1772		[window_object setStyleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask];
1773
1774		// Force update of the window styles
1775		NSRect frameRect = [window_object frame];
1776		[window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
1777		[window_object setFrame:frameRect display:NO];
1778
1779		// Restore the window title
1780		[window_object setTitle:[NSString stringWithUTF8String:title.utf8().get_data()]];
1781	}
1782
1783	_update_window();
1784
1785	[window_object makeKeyAndOrderFront:nil];
1786}
1787
1788bool OS_OSX::get_borderless_window() {
1789
1790	return [window_object styleMask] == NSWindowStyleMaskBorderless;
1791}
1792
1793String OS_OSX::get_executable_path() const {
1794
1795	int ret;
1796	pid_t pid;
1797	char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
1798
1799	pid = getpid();
1800	ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
1801	if (ret <= 0) {
1802		return OS::get_executable_path();
1803	} else {
1804		String path;
1805		path.parse_utf8(pathbuf);
1806
1807		return path;
1808	}
1809}
1810
1811// Returns string representation of keys, if they are printable.
1812//
1813static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) {
1814
1815	TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
1816	if (!currentKeyboard)
1817		return nil;
1818
1819	CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
1820	if (!layoutData)
1821		return nil;
1822
1823	const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
1824
1825	OSStatus err;
1826	CFMutableStringRef output = CFStringCreateMutable(NULL, 0);
1827
1828	for (int i = 0; i < length; ++i) {
1829
1830		UInt32 keysDown = 0;
1831		UniChar chars[4];
1832		UniCharCount realLength;
1833
1834		err = UCKeyTranslate(keyboardLayout,
1835				keyCode[i],
1836				kUCKeyActionDisplay,
1837				0,
1838				LMGetKbdType(),
1839				kUCKeyTranslateNoDeadKeysBit,
1840				&keysDown,
1841				sizeof(chars) / sizeof(chars[0]),
1842				&realLength,
1843				chars);
1844
1845		if (err != noErr) {
1846			CFRelease(output);
1847			return nil;
1848		}
1849
1850		CFStringAppendCharacters(output, chars, 1);
1851	}
1852
1853	//CFStringUppercase(output, NULL);
1854
1855	return (NSString *)output;
1856}
1857
1858OS::LatinKeyboardVariant OS_OSX::get_latin_keyboard_variant() const {
1859
1860	static LatinKeyboardVariant layout = LATIN_KEYBOARD_QWERTY;
1861
1862	if (keyboard_layout_dirty) {
1863
1864		layout = LATIN_KEYBOARD_QWERTY;
1865
1866		CGKeyCode keys[] = { kVK_ANSI_Q, kVK_ANSI_W, kVK_ANSI_E, kVK_ANSI_R, kVK_ANSI_T, kVK_ANSI_Y };
1867		NSString *test = createStringForKeys(keys, 6);
1868
1869		if ([test isEqualToString:@"qwertz"]) {
1870			layout = LATIN_KEYBOARD_QWERTZ;
1871		} else if ([test isEqualToString:@"azerty"]) {
1872			layout = LATIN_KEYBOARD_AZERTY;
1873		} else if ([test isEqualToString:@"qzerty"]) {
1874			layout = LATIN_KEYBOARD_QZERTY;
1875		} else if ([test isEqualToString:@"',.pyf"]) {
1876			layout = LATIN_KEYBOARD_DVORAK;
1877		} else if ([test isEqualToString:@"xvlcwk"]) {
1878			layout = LATIN_KEYBOARD_NEO;
1879		} else if ([test isEqualToString:@"qwfpgj"]) {
1880			layout = LATIN_KEYBOARD_COLEMAK;
1881		}
1882
1883		[test release];
1884
1885		keyboard_layout_dirty = false;
1886		return layout;
1887	}
1888
1889	return layout;
1890}
1891
1892void OS_OSX::process_events() {
1893
1894	while (true) {
1895		NSEvent *event = [NSApp
1896				nextEventMatchingMask:NSAnyEventMask
1897							untilDate:[NSDate distantPast]
1898							   inMode:NSDefaultRunLoopMode
1899							  dequeue:YES];
1900		if (event == nil)
1901			break;
1902
1903		[NSApp sendEvent:event];
1904	}
1905
1906	[autoreleasePool drain];
1907	autoreleasePool = [[NSAutoreleasePool alloc] init];
1908}
1909
1910void OS_OSX::push_input(const InputEvent &p_event) {
1911
1912	InputEvent ev = p_event;
1913	ev.ID = last_id++;
1914	input->parse_input_event(ev);
1915}
1916
1917void OS_OSX::run() {
1918
1919	force_quit = false;
1920
1921	if (!main_loop)
1922		return;
1923
1924	main_loop->init();
1925
1926	if (zoomed) {
1927		zoomed = false;
1928		set_window_fullscreen(true);
1929	}
1930
1931	//uint64_t last_ticks=get_ticks_usec();
1932
1933	//int frames=0;
1934	//uint64_t frame=0;
1935
1936	while (!force_quit) {
1937
1938		process_events(); // get rid of pending events
1939		last_id = joystick_osx->process_joysticks(last_id);
1940		if (Main::iteration() == true)
1941			break;
1942	};
1943
1944	main_loop->finish();
1945}
1946
1947void OS_OSX::set_mouse_mode(MouseMode p_mode) {
1948
1949	if (p_mode == mouse_mode)
1950		return;
1951
1952	if (p_mode == MOUSE_MODE_CAPTURED) {
1953		// Apple Docs state that the display parameter is not used.
1954		// "This parameter is not used. By default, you may pass kCGDirectMainDisplay."
1955		// https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html
1956		CGDisplayHideCursor(kCGDirectMainDisplay);
1957		CGAssociateMouseAndMouseCursorPosition(false);
1958	} else if (p_mode == MOUSE_MODE_HIDDEN) {
1959		CGDisplayHideCursor(kCGDirectMainDisplay);
1960		CGAssociateMouseAndMouseCursorPosition(true);
1961	} else {
1962		CGDisplayShowCursor(kCGDirectMainDisplay);
1963		CGAssociateMouseAndMouseCursorPosition(true);
1964	}
1965
1966	mouse_mode = p_mode;
1967}
1968
1969OS::MouseMode OS_OSX::get_mouse_mode() const {
1970
1971	return mouse_mode;
1972}
1973
1974String OS_OSX::get_joy_guid(int p_device) const {
1975	return input->get_joy_guid_remapped(p_device);
1976}
1977
1978Error OS_OSX::move_path_to_trash(String p_dir) {
1979	NSFileManager *fm = [NSFileManager defaultManager];
1980	NSURL *url = [NSURL fileURLWithPath:@(p_dir.utf8().get_data())];
1981	NSError *err;
1982
1983	if (![fm trashItemAtURL:url resultingItemURL:nil error:&err]) {
1984		ERR_PRINTS("trashItemAtURL error: " + String(err.localizedDescription.UTF8String));
1985		return FAILED;
1986	}
1987
1988	return OK;
1989}
1990
1991void OS_OSX::set_use_vsync(bool p_enable) {
1992	CGLContextObj ctx = CGLGetCurrentContext();
1993	if (ctx) {
1994		GLint swapInterval = p_enable ? 1 : 0;
1995		CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval);
1996	}
1997}
1998
1999bool OS_OSX::is_vsync_enabled() const {
2000	GLint swapInterval = 0;
2001	CGLContextObj ctx = CGLGetCurrentContext();
2002	if (ctx) {
2003		CGLGetParameter(ctx, kCGLCPSwapInterval, &swapInterval);
2004	}
2005	return swapInterval ? true : false;
2006}
2007
2008OS_OSX *OS_OSX::singleton = NULL;
2009
2010OS_OSX::OS_OSX() {
2011
2012	mouse_mode = OS::MOUSE_MODE_VISIBLE;
2013	main_loop = NULL;
2014	singleton = this;
2015	autoreleasePool = [[NSAutoreleasePool alloc] init];
2016
2017	eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
2018	ERR_FAIL_COND(!eventSource);
2019
2020	CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0);
2021
2022	/*
2023	if (pthread_key_create(&_Godot.nsgl.current, NULL) != 0) {
2024		_GodotInputError(Godot_PLATFORM_ERROR,
2025			"NSGL: Failed to create context TLS");
2026		return GL_FALSE;
2027	}
2028*/
2029
2030	framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
2031	ERR_FAIL_COND(!framework);
2032
2033	// Implicitly create shared NSApplication instance
2034	[GodotApplication sharedApplication];
2035
2036	// In case we are unbundled, make us a proper UI application
2037	[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
2038
2039	// Menu bar setup must go between sharedApplication above and
2040	// finishLaunching below, in order to properly emulate the behavior
2041	// of NSApplicationMain
2042	NSMenuItem *menu_item;
2043	NSString *title;
2044
2045	NSString *nsappname = [[[NSBundle mainBundle] performSelector:@selector(localizedInfoDictionary)] objectForKey:@"CFBundleName"];
2046	if (nsappname == nil)
2047		nsappname = [[NSProcessInfo processInfo] processName];
2048
2049	// Setup Apple menu
2050	NSMenu *apple_menu = [[NSMenu alloc] initWithTitle:@""];
2051	title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname];
2052	[apple_menu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
2053
2054	[apple_menu addItem:[NSMenuItem separatorItem]];
2055
2056	NSMenu *services = [[NSMenu alloc] initWithTitle:@""];
2057	menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""];
2058	[apple_menu setSubmenu:services forItem:menu_item];
2059	[NSApp setServicesMenu:services];
2060	[services release];
2061
2062	[apple_menu addItem:[NSMenuItem separatorItem]];
2063
2064	title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname];
2065	[apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
2066
2067	menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
2068	[menu_item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
2069
2070	[apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""];
2071
2072	[apple_menu addItem:[NSMenuItem separatorItem]];
2073
2074	title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
2075	[apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
2076
2077	// Setup menu bar
2078	NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""];
2079	menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
2080	[main_menu setSubmenu:apple_menu forItem:menu_item];
2081	[NSApp setMainMenu:main_menu];
2082
2083	[main_menu release];
2084	[apple_menu release];
2085
2086	[NSApp finishLaunching];
2087
2088	delegate = [[GodotApplicationDelegate alloc] init];
2089	ERR_FAIL_COND(!delegate);
2090	[NSApp setDelegate:delegate];
2091
2092	last_id = 1;
2093	cursor_shape = CURSOR_ARROW;
2094
2095	maximized = false;
2096	minimized = false;
2097	window_size = Vector2(1024, 600);
2098	zoomed = false;
2099	display_scale = 1.0;
2100}
2101
2102void OS_OSX::disable_crash_handler() {
2103	crash_handler.disable();
2104}
2105
2106bool OS_OSX::is_disable_crash_handler() const {
2107	return crash_handler.is_disabled();
2108}
2109