2
0

os_osx.mm 99 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473
  1. /*************************************************************************/
  2. /* os_osx.mm */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 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. #include "core/math/geometry.h"
  32. #include "core/os/keyboard.h"
  33. #include "core/print_string.h"
  34. #include "core/version_generated.gen.h"
  35. #include "dir_access_osx.h"
  36. #include "drivers/gles2/rasterizer_gles2.h"
  37. #include "drivers/gles3/rasterizer_gles3.h"
  38. #include "main/main.h"
  39. #include "servers/visual/visual_server_raster.h"
  40. #include <mach-o/dyld.h>
  41. #include <Carbon/Carbon.h>
  42. #import <Cocoa/Cocoa.h>
  43. #include <IOKit/IOCFPlugIn.h>
  44. #include <IOKit/IOKitLib.h>
  45. #include <IOKit/hid/IOHIDKeys.h>
  46. #include <IOKit/hid/IOHIDLib.h>
  47. #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
  48. #include <os/log.h>
  49. #endif
  50. #include <dlfcn.h>
  51. #include <fcntl.h>
  52. #include <libproc.h>
  53. #include <stdio.h>
  54. #include <stdlib.h>
  55. #include <sys/stat.h>
  56. #include <sys/types.h>
  57. #include <unistd.h>
  58. #if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
  59. #define NSEventMaskAny NSAnyEventMask
  60. #define NSEventTypeKeyDown NSKeyDown
  61. #define NSEventTypeKeyUp NSKeyUp
  62. #define NSEventModifierFlagShift NSShiftKeyMask
  63. #define NSEventModifierFlagCommand NSCommandKeyMask
  64. #define NSEventModifierFlagControl NSControlKeyMask
  65. #define NSEventModifierFlagOption NSAlternateKeyMask
  66. #define NSWindowStyleMaskTitled NSTitledWindowMask
  67. #define NSWindowStyleMaskResizable NSResizableWindowMask
  68. #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
  69. #define NSWindowStyleMaskClosable NSClosableWindowMask
  70. #define NSWindowStyleMaskBorderless NSBorderlessWindowMask
  71. #endif
  72. #ifndef NSAppKitVersionNumber10_12
  73. #define NSAppKitVersionNumber10_12 1504
  74. #endif
  75. #ifndef NSAppKitVersionNumber10_14
  76. #define NSAppKitVersionNumber10_14 1671
  77. #endif
  78. static void get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> state) {
  79. state->set_shift((p_osx_state & NSEventModifierFlagShift));
  80. state->set_control((p_osx_state & NSEventModifierFlagControl));
  81. state->set_alt((p_osx_state & NSEventModifierFlagOption));
  82. state->set_metakey((p_osx_state & NSEventModifierFlagCommand));
  83. }
  84. static void push_to_key_event_buffer(const OS_OSX::KeyEvent &p_event) {
  85. Vector<OS_OSX::KeyEvent> &buffer = OS_OSX::singleton->key_event_buffer;
  86. if (OS_OSX::singleton->key_event_pos >= buffer.size()) {
  87. buffer.resize(1 + OS_OSX::singleton->key_event_pos);
  88. }
  89. buffer.write[OS_OSX::singleton->key_event_pos++] = p_event;
  90. }
  91. static int mouse_x = 0;
  92. static int mouse_y = 0;
  93. static int button_mask = 0;
  94. static bool mouse_down_control = false;
  95. static bool ignore_momentum_scroll = false;
  96. static Vector2 get_mouse_pos(NSPoint locationInWindow) {
  97. const NSRect contentRect = [OS_OSX::singleton->window_view frame];
  98. const NSPoint p = locationInWindow;
  99. const float s = OS_OSX::singleton->get_screen_max_scale();
  100. mouse_x = p.x * s;
  101. mouse_y = (contentRect.size.height - p.y) * s;
  102. return Vector2(mouse_x, mouse_y);
  103. }
  104. static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
  105. if ([NSCursor respondsToSelector:selector]) {
  106. id object = [NSCursor performSelector:selector];
  107. if ([object isKindOfClass:[NSCursor class]]) {
  108. return object;
  109. }
  110. }
  111. if (fallback) {
  112. // Fallback should be a reasonable default, no need to check.
  113. return [NSCursor performSelector:fallback];
  114. }
  115. return [NSCursor arrowCursor];
  116. }
  117. @interface GodotApplication : NSApplication
  118. @end
  119. @implementation GodotApplication
  120. - (void)sendEvent:(NSEvent *)event {
  121. // special case handling of command-period, which is traditionally a special
  122. // shortcut in macOS and doesn't arrive at our regular keyDown handler.
  123. if ([event type] == NSEventTypeKeyDown) {
  124. if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
  125. Ref<InputEventKey> k;
  126. k.instance();
  127. get_key_modifier_state([event modifierFlags], k);
  128. k->set_pressed(true);
  129. k->set_scancode(KEY_PERIOD);
  130. k->set_physical_scancode(KEY_PERIOD);
  131. k->set_echo([event isARepeat]);
  132. OS_OSX::singleton->push_input(k);
  133. }
  134. }
  135. // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
  136. // This works around an AppKit bug, where key up events while holding
  137. // down the command key don't get sent to the key window.
  138. if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand))
  139. [[self keyWindow] sendEvent:event];
  140. else
  141. [super sendEvent:event];
  142. }
  143. @end
  144. @interface GodotApplicationDelegate : NSObject
  145. - (void)forceUnbundledWindowActivationHackStep1;
  146. - (void)forceUnbundledWindowActivationHackStep2;
  147. - (void)forceUnbundledWindowActivationHackStep3;
  148. @end
  149. @implementation GodotApplicationDelegate
  150. - (void)forceUnbundledWindowActivationHackStep1 {
  151. // Step1: Switch focus to macOS Dock.
  152. // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
  153. for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
  154. [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
  155. break;
  156. }
  157. [self performSelector:@selector(forceUnbundledWindowActivationHackStep2) withObject:nil afterDelay:0.02];
  158. }
  159. - (void)forceUnbundledWindowActivationHackStep2 {
  160. // Step 2: Register app as foreground process.
  161. ProcessSerialNumber psn = { 0, kCurrentProcess };
  162. (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
  163. [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
  164. }
  165. - (void)forceUnbundledWindowActivationHackStep3 {
  166. // Step 3: Switch focus back to app window.
  167. [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
  168. }
  169. - (void)applicationDidFinishLaunching:(NSNotification *)notice {
  170. NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
  171. if (nsappname == nil) {
  172. // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
  173. [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
  174. }
  175. }
  176. - (void)globalMenuCallback:(id)sender {
  177. if (![sender representedObject])
  178. return;
  179. OS_OSX::GlobalMenuItem *item = (OS_OSX::GlobalMenuItem *)[[sender representedObject] pointerValue];
  180. if (!item)
  181. return;
  182. OS_OSX::singleton->main_loop->global_menu_action(item->signal, item->meta);
  183. }
  184. - (NSMenu *)applicationDockMenu:(NSApplication *)sender {
  185. NSMenu *menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
  186. Vector<OS_OSX::GlobalMenuItem> &E = OS_OSX::singleton->global_menus["_dock"];
  187. for (int i = 0; i < E.size(); i++) {
  188. if (E[i].label == String()) {
  189. [menu addItem:[NSMenuItem separatorItem]];
  190. } else {
  191. NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:E[i].label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
  192. [menu_item setRepresentedObject:[NSValue valueWithPointer:&(E[i])]];
  193. }
  194. }
  195. return menu;
  196. }
  197. - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
  198. // Note: may be called called before main loop init!
  199. char *utfs = strdup([filename UTF8String]);
  200. OS_OSX::singleton->open_with_filename.parse_utf8(utfs);
  201. free(utfs);
  202. #ifdef TOOLS_ENABLED
  203. // Open new instance
  204. if (OS_OSX::singleton->get_main_loop()) {
  205. List<String> args;
  206. args.push_back(OS_OSX::singleton->open_with_filename);
  207. String exec = OS::get_singleton()->get_executable_path();
  208. OS::ProcessID pid = 0;
  209. OS::get_singleton()->execute(exec, args, false, &pid);
  210. }
  211. #endif
  212. return YES;
  213. }
  214. - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
  215. if (OS_OSX::singleton->get_main_loop())
  216. OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
  217. return NSTerminateCancel;
  218. }
  219. - (void)applicationDidHide:(NSNotification *)notification {
  220. /*
  221. _Godotwindow* window;
  222. for (window = _Godot.windowListHead; window; window = window->next)
  223. _GodotInputWindowVisibility(window, GL_FALSE);
  224. */
  225. }
  226. - (void)applicationDidUnhide:(NSNotification *)notification {
  227. /*
  228. _Godotwindow* window;
  229. for (window = _Godot.windowListHead; window; window = window->next) {
  230. if ([window_object isVisible])
  231. _GodotInputWindowVisibility(window, GL_TRUE);
  232. }
  233. */
  234. }
  235. - (void)applicationDidChangeScreenParameters:(NSNotification *)notification {
  236. //_GodotInputMonitorChange();
  237. }
  238. - (void)showAbout:(id)sender {
  239. if (OS_OSX::singleton->get_main_loop())
  240. OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
  241. }
  242. @end
  243. @interface GodotWindowDelegate : NSObject {
  244. //_Godotwindow* window;
  245. }
  246. @end
  247. @implementation GodotWindowDelegate
  248. - (BOOL)windowShouldClose:(id)sender {
  249. //_GodotInputWindowCloseRequest(window);
  250. if (OS_OSX::singleton->get_main_loop())
  251. OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
  252. return NO;
  253. }
  254. - (void)windowDidEnterFullScreen:(NSNotification *)notification {
  255. OS_OSX::singleton->zoomed = true;
  256. [OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(0, 0)];
  257. [OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
  258. }
  259. - (void)windowDidExitFullScreen:(NSNotification *)notification {
  260. OS_OSX::singleton->zoomed = false;
  261. if (OS_OSX::singleton->min_size != Size2()) {
  262. Size2 size = OS_OSX::singleton->min_size / OS_OSX::singleton->get_screen_max_scale();
  263. [OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(size.x, size.y)];
  264. }
  265. if (OS_OSX::singleton->max_size != Size2()) {
  266. Size2 size = OS_OSX::singleton->max_size / OS_OSX::singleton->get_screen_max_scale();
  267. [OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
  268. }
  269. if (!OS_OSX::singleton->resizable)
  270. [OS_OSX::singleton->window_object setStyleMask:[OS_OSX::singleton->window_object styleMask] & ~NSWindowStyleMaskResizable];
  271. if (OS_OSX::singleton->on_top)
  272. [OS_OSX::singleton->window_object setLevel:NSFloatingWindowLevel];
  273. }
  274. - (void)windowDidChangeBackingProperties:(NSNotification *)notification {
  275. if (!OS_OSX::singleton)
  276. return;
  277. NSWindow *window = (NSWindow *)[notification object];
  278. CGFloat newBackingScaleFactor = [window backingScaleFactor];
  279. CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
  280. if (OS_OSX::singleton->is_hidpi_allowed()) {
  281. [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:YES];
  282. } else {
  283. [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:NO];
  284. }
  285. if (newBackingScaleFactor != oldBackingScaleFactor) {
  286. //Set new display scale and window size
  287. float newDisplayScale = OS_OSX::singleton->get_screen_max_scale();
  288. const NSRect contentRect = [OS_OSX::singleton->window_view frame];
  289. const NSRect fbRect = contentRect;
  290. OS_OSX::singleton->window_size.width = fbRect.size.width * newDisplayScale;
  291. OS_OSX::singleton->window_size.height = fbRect.size.height * newDisplayScale;
  292. if (OS_OSX::singleton->context) {
  293. GLint dim[2];
  294. dim[0] = OS_OSX::singleton->window_size.width;
  295. dim[1] = OS_OSX::singleton->window_size.height;
  296. CGLSetParameter((CGLContextObj)[OS_OSX::singleton->context CGLContextObj], kCGLCPSurfaceBackingSize, &dim[0]);
  297. CGLEnable((CGLContextObj)[OS_OSX::singleton->context CGLContextObj], kCGLCESurfaceBackingSize);
  298. }
  299. //Update context
  300. if (OS_OSX::singleton->main_loop) {
  301. //Force window resize event
  302. [self windowDidResize:notification];
  303. }
  304. }
  305. }
  306. - (void)windowDidResize:(NSNotification *)notification {
  307. [OS_OSX::singleton->context update];
  308. const NSRect contentRect = [OS_OSX::singleton->window_view frame];
  309. const NSRect fbRect = contentRect;
  310. float displayScale = OS_OSX::singleton->get_screen_max_scale();
  311. OS_OSX::singleton->window_size.width = fbRect.size.width * displayScale;
  312. OS_OSX::singleton->window_size.height = fbRect.size.height * displayScale;
  313. if (OS_OSX::singleton->context) {
  314. GLint dim[2];
  315. dim[0] = OS_OSX::singleton->window_size.width;
  316. dim[1] = OS_OSX::singleton->window_size.height;
  317. CGLSetParameter((CGLContextObj)[OS_OSX::singleton->context CGLContextObj], kCGLCPSurfaceBackingSize, &dim[0]);
  318. CGLEnable((CGLContextObj)[OS_OSX::singleton->context CGLContextObj], kCGLCESurfaceBackingSize);
  319. }
  320. if (OS_OSX::singleton->main_loop) {
  321. Main::force_redraw();
  322. //Event retrieval blocks until resize is over. Call Main::iteration() directly.
  323. if (!Main::is_iterating()) { //avoid cyclic loop
  324. Main::iteration();
  325. }
  326. }
  327. /*
  328. _GodotInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
  329. _GodotInputWindowSize(window, contentRect.size.width, contentRect.size.height);
  330. _GodotInputWindowDamage(window);
  331. if (window->cursorMode == Godot_CURSOR_DISABLED)
  332. centerCursor(window);
  333. */
  334. }
  335. - (void)windowDidMove:(NSNotification *)notification {
  336. if (OS_OSX::singleton->get_main_loop()) {
  337. OS_OSX::singleton->input->release_pressed_events();
  338. }
  339. /*
  340. [window->nsgl.context update];
  341. int x, y;
  342. _GodotPlatformGetWindowPos(window, &x, &y);
  343. _GodotInputWindowPos(window, x, y);
  344. if (window->cursorMode == Godot_CURSOR_DISABLED)
  345. centerCursor(window);
  346. */
  347. }
  348. - (void)windowDidBecomeKey:(NSNotification *)notification {
  349. if (OS_OSX::singleton->get_main_loop()) {
  350. get_mouse_pos([OS_OSX::singleton->window_object mouseLocationOutsideOfEventStream]);
  351. OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y));
  352. OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
  353. }
  354. OS_OSX::singleton->window_focused = true;
  355. }
  356. - (void)windowDidResignKey:(NSNotification *)notification {
  357. if (OS_OSX::singleton->get_main_loop())
  358. OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
  359. OS_OSX::singleton->window_focused = false;
  360. }
  361. - (void)windowDidMiniaturize:(NSNotification *)notification {
  362. OS_OSX::singleton->wm_minimized(true);
  363. if (OS_OSX::singleton->get_main_loop())
  364. OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
  365. OS_OSX::singleton->window_focused = false;
  366. };
  367. - (void)windowDidDeminiaturize:(NSNotification *)notification {
  368. OS_OSX::singleton->wm_minimized(false);
  369. if (OS_OSX::singleton->get_main_loop())
  370. OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
  371. OS_OSX::singleton->window_focused = true;
  372. };
  373. @end
  374. @interface GodotContentView : NSOpenGLView <NSTextInputClient> {
  375. NSTrackingArea *trackingArea;
  376. NSMutableAttributedString *markedText;
  377. bool imeInputEventInProgress;
  378. }
  379. - (void)cancelComposition;
  380. - (BOOL)wantsUpdateLayer;
  381. - (void)updateLayer;
  382. @end
  383. @implementation GodotContentView
  384. + (void)initialize {
  385. if (self == [GodotContentView class]) {
  386. // nothing left to do here at the moment..
  387. }
  388. }
  389. - (BOOL)wantsUpdateLayer {
  390. return YES;
  391. }
  392. - (void)updateLayer {
  393. [OS_OSX::singleton->context update];
  394. }
  395. - (id)init {
  396. self = [super init];
  397. trackingArea = nil;
  398. imeInputEventInProgress = false;
  399. [self updateTrackingAreas];
  400. #if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
  401. [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
  402. #else
  403. [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
  404. #endif
  405. markedText = [[NSMutableAttributedString alloc] init];
  406. return self;
  407. }
  408. - (void)dealloc {
  409. [trackingArea release];
  410. [markedText release];
  411. [super dealloc];
  412. }
  413. static const NSRange kEmptyRange = { NSNotFound, 0 };
  414. - (BOOL)hasMarkedText {
  415. return (markedText.length > 0);
  416. }
  417. - (NSRange)markedRange {
  418. return NSMakeRange(0, markedText.length);
  419. }
  420. - (NSRange)selectedRange {
  421. return kEmptyRange;
  422. }
  423. - (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {
  424. if ([aString isKindOfClass:[NSAttributedString class]]) {
  425. [markedText initWithAttributedString:aString];
  426. } else {
  427. [markedText initWithString:aString];
  428. }
  429. if (markedText.length == 0) {
  430. [self unmarkText];
  431. return;
  432. }
  433. if (OS_OSX::singleton->im_active) {
  434. imeInputEventInProgress = true;
  435. OS_OSX::singleton->im_text.parse_utf8([[markedText mutableString] UTF8String]);
  436. OS_OSX::singleton->im_selection = Point2(selectedRange.location, selectedRange.length);
  437. if (OS_OSX::singleton->get_main_loop())
  438. OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
  439. }
  440. }
  441. - (void)doCommandBySelector:(SEL)aSelector {
  442. if ([self respondsToSelector:aSelector])
  443. [self performSelector:aSelector];
  444. }
  445. - (void)unmarkText {
  446. imeInputEventInProgress = false;
  447. [[markedText mutableString] setString:@""];
  448. if (OS_OSX::singleton->im_active) {
  449. OS_OSX::singleton->im_text = String();
  450. OS_OSX::singleton->im_selection = Point2();
  451. if (OS_OSX::singleton->get_main_loop())
  452. OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
  453. }
  454. }
  455. - (NSArray *)validAttributesForMarkedText {
  456. return [NSArray array];
  457. }
  458. - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
  459. return nil;
  460. }
  461. - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
  462. return 0;
  463. }
  464. - (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
  465. const NSRect contentRect = [OS_OSX::singleton->window_view frame];
  466. float displayScale = OS_OSX::singleton->get_screen_max_scale();
  467. NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / displayScale, contentRect.size.height - (OS_OSX::singleton->im_position.y / displayScale) - 1, 0, 0);
  468. NSPoint pointOnScreen = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin;
  469. return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0);
  470. }
  471. - (void)cancelComposition {
  472. [self unmarkText];
  473. NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
  474. [currentInputContext discardMarkedText];
  475. }
  476. - (void)insertText:(id)aString {
  477. [self insertText:aString replacementRange:NSMakeRange(0, 0)];
  478. }
  479. - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
  480. NSEvent *event = [NSApp currentEvent];
  481. NSString *characters;
  482. if ([aString isKindOfClass:[NSAttributedString class]]) {
  483. characters = [aString string];
  484. } else {
  485. characters = (NSString *)aString;
  486. }
  487. NSUInteger i, length = [characters length];
  488. NSCharacterSet *ctrlChars = [NSCharacterSet controlCharacterSet];
  489. NSCharacterSet *wsnlChars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
  490. if ([characters rangeOfCharacterFromSet:ctrlChars].length && [characters rangeOfCharacterFromSet:wsnlChars].length == 0) {
  491. NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
  492. [currentInputContext discardMarkedText];
  493. [self cancelComposition];
  494. return;
  495. }
  496. for (i = 0; i < length; i++) {
  497. const unichar codepoint = [characters characterAtIndex:i];
  498. if ((codepoint & 0xFF00) == 0xF700)
  499. continue;
  500. OS_OSX::KeyEvent ke;
  501. ke.osx_state = [event modifierFlags];
  502. ke.pressed = true;
  503. ke.echo = false;
  504. ke.raw = false; // IME input event
  505. ke.scancode = 0;
  506. ke.physical_scancode = 0;
  507. ke.unicode = codepoint;
  508. push_to_key_event_buffer(ke);
  509. }
  510. [self cancelComposition];
  511. }
  512. - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
  513. return NSDragOperationCopy;
  514. }
  515. - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
  516. return NSDragOperationCopy;
  517. }
  518. - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
  519. Vector<String> files;
  520. NSPasteboard *pboard = [sender draggingPasteboard];
  521. #if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
  522. NSArray *items = pboard.pasteboardItems;
  523. for (NSPasteboardItem *item in items) {
  524. NSString *path = [item stringForType:NSPasteboardTypeFileURL];
  525. NSString *ns = [NSURL URLWithString:path].path;
  526. char *utfs = strdup([ns UTF8String]);
  527. String ret;
  528. ret.parse_utf8(utfs);
  529. free(utfs);
  530. files.push_back(ret);
  531. }
  532. #else
  533. NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
  534. for (NSString *ns in filenames) {
  535. char *utfs = strdup([ns UTF8String]);
  536. String ret;
  537. ret.parse_utf8(utfs);
  538. free(utfs);
  539. files.push_back(ret);
  540. }
  541. #endif
  542. if (files.size()) {
  543. OS_OSX::singleton->main_loop->drop_files(files, 0);
  544. OS_OSX::singleton->move_window_to_foreground();
  545. }
  546. return NO;
  547. }
  548. - (BOOL)isOpaque {
  549. return YES;
  550. }
  551. - (BOOL)canBecomeKeyView {
  552. return YES;
  553. }
  554. - (BOOL)acceptsFirstResponder {
  555. return YES;
  556. }
  557. - (void)cursorUpdate:(NSEvent *)event {
  558. OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
  559. OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
  560. OS_OSX::singleton->set_cursor_shape(p_shape);
  561. }
  562. static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
  563. if (pressed) {
  564. button_mask |= mask;
  565. } else {
  566. button_mask &= ~mask;
  567. }
  568. Ref<InputEventMouseButton> mb;
  569. mb.instance();
  570. const Vector2 pos = get_mouse_pos([event locationInWindow]);
  571. get_key_modifier_state([event modifierFlags], mb);
  572. mb->set_button_index(index);
  573. mb->set_pressed(pressed);
  574. mb->set_position(pos);
  575. mb->set_global_position(pos);
  576. mb->set_button_mask(button_mask);
  577. if (index == BUTTON_LEFT && pressed) {
  578. mb->set_doubleclick([event clickCount] == 2);
  579. }
  580. OS_OSX::singleton->push_input(mb);
  581. }
  582. - (void)mouseDown:(NSEvent *)event {
  583. if (([event modifierFlags] & NSEventModifierFlagControl)) {
  584. mouse_down_control = true;
  585. _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
  586. } else {
  587. mouse_down_control = false;
  588. _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, true);
  589. }
  590. }
  591. - (void)mouseDragged:(NSEvent *)event {
  592. [self mouseMoved:event];
  593. }
  594. - (void)mouseUp:(NSEvent *)event {
  595. if (mouse_down_control) {
  596. _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
  597. } else {
  598. _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, false);
  599. }
  600. }
  601. - (void)mouseMoved:(NSEvent *)event {
  602. NSPoint delta = NSMakePoint([event deltaX], [event deltaY]);
  603. NSPoint mpos = [event locationInWindow];
  604. if (OS_OSX::singleton->ignore_warp) {
  605. // Discard late events, before warp
  606. if (([event timestamp]) < OS_OSX::singleton->last_warp) {
  607. return;
  608. }
  609. OS_OSX::singleton->ignore_warp = false;
  610. return;
  611. }
  612. if (OS_OSX::singleton->mouse_mode == OS::MOUSE_MODE_CONFINED) {
  613. // Discard late events
  614. if (([event timestamp]) < OS_OSX::singleton->last_warp) {
  615. return;
  616. }
  617. // Warp affects next event delta, subtract previous warp deltas
  618. List<OS_OSX::WarpEvent>::Element *F = OS_OSX::singleton->warp_events.front();
  619. while (F) {
  620. if (F->get().timestamp < [event timestamp]) {
  621. List<OS_OSX::WarpEvent>::Element *E = F;
  622. delta.x -= E->get().delta.x;
  623. delta.y -= E->get().delta.y;
  624. F = F->next();
  625. OS_OSX::singleton->warp_events.erase(E);
  626. } else {
  627. F = F->next();
  628. }
  629. }
  630. // Confine mouse position to the window, and update delta
  631. NSRect frame = [OS_OSX::singleton->window_object frame];
  632. NSPoint conf_pos = mpos;
  633. conf_pos.x = CLAMP(conf_pos.x + delta.x, 0.f, frame.size.width);
  634. conf_pos.y = CLAMP(conf_pos.y - delta.y, 0.f, frame.size.height);
  635. delta.x = conf_pos.x - mpos.x;
  636. delta.y = mpos.y - conf_pos.y;
  637. mpos = conf_pos;
  638. // Move mouse cursor
  639. NSRect pointInWindowRect = NSMakeRect(conf_pos.x, conf_pos.y, 0, 0);
  640. conf_pos = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin;
  641. conf_pos.y = CGDisplayBounds(CGMainDisplayID()).size.height - conf_pos.y;
  642. CGWarpMouseCursorPosition(conf_pos);
  643. // Save warp data
  644. OS_OSX::singleton->last_warp = [[NSProcessInfo processInfo] systemUptime];
  645. OS_OSX::WarpEvent ev;
  646. ev.timestamp = OS_OSX::singleton->last_warp;
  647. ev.delta = delta;
  648. OS_OSX::singleton->warp_events.push_back(ev);
  649. }
  650. Ref<InputEventMouseMotion> mm;
  651. mm.instance();
  652. mm->set_button_mask(button_mask);
  653. const Vector2 pos = get_mouse_pos(mpos);
  654. mm->set_position(pos);
  655. mm->set_pressure([event pressure]);
  656. if ([event subtype] == NSEventSubtypeTabletPoint) {
  657. const NSPoint p = [event tilt];
  658. mm->set_tilt(Vector2(p.x, p.y));
  659. }
  660. mm->set_global_position(pos);
  661. mm->set_speed(OS_OSX::singleton->input->get_last_mouse_speed());
  662. const Vector2 relativeMotion = Vector2(delta.x, delta.y) * OS_OSX::singleton->get_screen_max_scale();
  663. mm->set_relative(relativeMotion);
  664. get_key_modifier_state([event modifierFlags], mm);
  665. OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y));
  666. OS_OSX::singleton->push_input(mm);
  667. }
  668. - (void)rightMouseDown:(NSEvent *)event {
  669. _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
  670. }
  671. - (void)rightMouseDragged:(NSEvent *)event {
  672. [self mouseMoved:event];
  673. }
  674. - (void)rightMouseUp:(NSEvent *)event {
  675. _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
  676. }
  677. - (void)otherMouseDown:(NSEvent *)event {
  678. if ((int)[event buttonNumber] == 2) {
  679. _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true);
  680. } else if ((int)[event buttonNumber] == 3) {
  681. _mouseDownEvent(event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, true);
  682. } else if ((int)[event buttonNumber] == 4) {
  683. _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, true);
  684. } else {
  685. return;
  686. }
  687. }
  688. - (void)otherMouseDragged:(NSEvent *)event {
  689. [self mouseMoved:event];
  690. }
  691. - (void)otherMouseUp:(NSEvent *)event {
  692. if ((int)[event buttonNumber] == 2) {
  693. _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false);
  694. } else if ((int)[event buttonNumber] == 3) {
  695. _mouseDownEvent(event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, false);
  696. } else if ((int)[event buttonNumber] == 4) {
  697. _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, false);
  698. } else {
  699. return;
  700. }
  701. }
  702. - (void)mouseExited:(NSEvent *)event {
  703. if (!OS_OSX::singleton)
  704. return;
  705. if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
  706. OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
  707. }
  708. - (void)mouseEntered:(NSEvent *)event {
  709. if (!OS_OSX::singleton)
  710. return;
  711. if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
  712. OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
  713. OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
  714. OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
  715. OS_OSX::singleton->set_cursor_shape(p_shape);
  716. }
  717. - (void)magnifyWithEvent:(NSEvent *)event {
  718. Ref<InputEventMagnifyGesture> ev;
  719. ev.instance();
  720. get_key_modifier_state([event modifierFlags], ev);
  721. ev->set_position(get_mouse_pos([event locationInWindow]));
  722. ev->set_factor([event magnification] + 1.0);
  723. OS_OSX::singleton->push_input(ev);
  724. }
  725. - (void)viewDidChangeBackingProperties {
  726. // nothing left to do here
  727. }
  728. - (void)updateTrackingAreas {
  729. if (trackingArea != nil) {
  730. [self removeTrackingArea:trackingArea];
  731. [trackingArea release];
  732. }
  733. NSTrackingAreaOptions options =
  734. NSTrackingMouseEnteredAndExited |
  735. NSTrackingActiveInKeyWindow |
  736. NSTrackingCursorUpdate |
  737. NSTrackingInVisibleRect;
  738. trackingArea = [[NSTrackingArea alloc]
  739. initWithRect:[self bounds]
  740. options:options
  741. owner:self
  742. userInfo:nil];
  743. [self addTrackingArea:trackingArea];
  744. [super updateTrackingAreas];
  745. }
  746. static bool isNumpadKey(unsigned int key) {
  747. static const unsigned int table[] = {
  748. 0x41, /* kVK_ANSI_KeypadDecimal */
  749. 0x43, /* kVK_ANSI_KeypadMultiply */
  750. 0x45, /* kVK_ANSI_KeypadPlus */
  751. 0x47, /* kVK_ANSI_KeypadClear */
  752. 0x4b, /* kVK_ANSI_KeypadDivide */
  753. 0x4c, /* kVK_ANSI_KeypadEnter */
  754. 0x4e, /* kVK_ANSI_KeypadMinus */
  755. 0x51, /* kVK_ANSI_KeypadEquals */
  756. 0x52, /* kVK_ANSI_Keypad0 */
  757. 0x53, /* kVK_ANSI_Keypad1 */
  758. 0x54, /* kVK_ANSI_Keypad2 */
  759. 0x55, /* kVK_ANSI_Keypad3 */
  760. 0x56, /* kVK_ANSI_Keypad4 */
  761. 0x57, /* kVK_ANSI_Keypad5 */
  762. 0x58, /* kVK_ANSI_Keypad6 */
  763. 0x59, /* kVK_ANSI_Keypad7 */
  764. 0x5b, /* kVK_ANSI_Keypad8 */
  765. 0x5c, /* kVK_ANSI_Keypad9 */
  766. 0x5f, /* kVK_JIS_KeypadComma */
  767. 0x00
  768. };
  769. for (int i = 0; table[i] != 0; i++) {
  770. if (key == table[i])
  771. return true;
  772. }
  773. return false;
  774. }
  775. // Translates a OS X keycode to a Godot keycode
  776. //
  777. static int translateKey(unsigned int key) {
  778. // Keyboard symbol translation table
  779. static const unsigned int table[128] = {
  780. /* 00 */ KEY_A,
  781. /* 01 */ KEY_S,
  782. /* 02 */ KEY_D,
  783. /* 03 */ KEY_F,
  784. /* 04 */ KEY_H,
  785. /* 05 */ KEY_G,
  786. /* 06 */ KEY_Z,
  787. /* 07 */ KEY_X,
  788. /* 08 */ KEY_C,
  789. /* 09 */ KEY_V,
  790. /* 0a */ KEY_SECTION, /* ISO Section */
  791. /* 0b */ KEY_B,
  792. /* 0c */ KEY_Q,
  793. /* 0d */ KEY_W,
  794. /* 0e */ KEY_E,
  795. /* 0f */ KEY_R,
  796. /* 10 */ KEY_Y,
  797. /* 11 */ KEY_T,
  798. /* 12 */ KEY_1,
  799. /* 13 */ KEY_2,
  800. /* 14 */ KEY_3,
  801. /* 15 */ KEY_4,
  802. /* 16 */ KEY_6,
  803. /* 17 */ KEY_5,
  804. /* 18 */ KEY_EQUAL,
  805. /* 19 */ KEY_9,
  806. /* 1a */ KEY_7,
  807. /* 1b */ KEY_MINUS,
  808. /* 1c */ KEY_8,
  809. /* 1d */ KEY_0,
  810. /* 1e */ KEY_BRACERIGHT,
  811. /* 1f */ KEY_O,
  812. /* 20 */ KEY_U,
  813. /* 21 */ KEY_BRACELEFT,
  814. /* 22 */ KEY_I,
  815. /* 23 */ KEY_P,
  816. /* 24 */ KEY_ENTER,
  817. /* 25 */ KEY_L,
  818. /* 26 */ KEY_J,
  819. /* 27 */ KEY_APOSTROPHE,
  820. /* 28 */ KEY_K,
  821. /* 29 */ KEY_SEMICOLON,
  822. /* 2a */ KEY_BACKSLASH,
  823. /* 2b */ KEY_COMMA,
  824. /* 2c */ KEY_SLASH,
  825. /* 2d */ KEY_N,
  826. /* 2e */ KEY_M,
  827. /* 2f */ KEY_PERIOD,
  828. /* 30 */ KEY_TAB,
  829. /* 31 */ KEY_SPACE,
  830. /* 32 */ KEY_QUOTELEFT,
  831. /* 33 */ KEY_BACKSPACE,
  832. /* 34 */ KEY_UNKNOWN,
  833. /* 35 */ KEY_ESCAPE,
  834. /* 36 */ KEY_META,
  835. /* 37 */ KEY_META,
  836. /* 38 */ KEY_SHIFT,
  837. /* 39 */ KEY_CAPSLOCK,
  838. /* 3a */ KEY_ALT,
  839. /* 3b */ KEY_CONTROL,
  840. /* 3c */ KEY_SHIFT,
  841. /* 3d */ KEY_ALT,
  842. /* 3e */ KEY_CONTROL,
  843. /* 3f */ KEY_UNKNOWN, /* Function */
  844. /* 40 */ KEY_UNKNOWN, /* F17 */
  845. /* 41 */ KEY_KP_PERIOD,
  846. /* 42 */ KEY_UNKNOWN,
  847. /* 43 */ KEY_KP_MULTIPLY,
  848. /* 44 */ KEY_UNKNOWN,
  849. /* 45 */ KEY_KP_ADD,
  850. /* 46 */ KEY_UNKNOWN,
  851. /* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */
  852. /* 48 */ KEY_VOLUMEUP, /* VolumeUp */
  853. /* 49 */ KEY_VOLUMEDOWN, /* VolumeDown */
  854. /* 4a */ KEY_VOLUMEMUTE, /* Mute */
  855. /* 4b */ KEY_KP_DIVIDE,
  856. /* 4c */ KEY_KP_ENTER,
  857. /* 4d */ KEY_UNKNOWN,
  858. /* 4e */ KEY_KP_SUBTRACT,
  859. /* 4f */ KEY_UNKNOWN, /* F18 */
  860. /* 50 */ KEY_UNKNOWN, /* F19 */
  861. /* 51 */ KEY_EQUAL, /* KeypadEqual */
  862. /* 52 */ KEY_KP_0,
  863. /* 53 */ KEY_KP_1,
  864. /* 54 */ KEY_KP_2,
  865. /* 55 */ KEY_KP_3,
  866. /* 56 */ KEY_KP_4,
  867. /* 57 */ KEY_KP_5,
  868. /* 58 */ KEY_KP_6,
  869. /* 59 */ KEY_KP_7,
  870. /* 5a */ KEY_UNKNOWN, /* F20 */
  871. /* 5b */ KEY_KP_8,
  872. /* 5c */ KEY_KP_9,
  873. /* 5d */ KEY_YEN, /* JIS Yen */
  874. /* 5e */ KEY_UNDERSCORE, /* JIS Underscore */
  875. /* 5f */ KEY_COMMA, /* JIS KeypadComma */
  876. /* 60 */ KEY_F5,
  877. /* 61 */ KEY_F6,
  878. /* 62 */ KEY_F7,
  879. /* 63 */ KEY_F3,
  880. /* 64 */ KEY_F8,
  881. /* 65 */ KEY_F9,
  882. /* 66 */ KEY_UNKNOWN, /* JIS Eisu */
  883. /* 67 */ KEY_F11,
  884. /* 68 */ KEY_UNKNOWN, /* JIS Kana */
  885. /* 69 */ KEY_F13,
  886. /* 6a */ KEY_F16,
  887. /* 6b */ KEY_F14,
  888. /* 6c */ KEY_UNKNOWN,
  889. /* 6d */ KEY_F10,
  890. /* 6e */ KEY_MENU,
  891. /* 6f */ KEY_F12,
  892. /* 70 */ KEY_UNKNOWN,
  893. /* 71 */ KEY_F15,
  894. /* 72 */ KEY_INSERT, /* Really Help... */
  895. /* 73 */ KEY_HOME,
  896. /* 74 */ KEY_PAGEUP,
  897. /* 75 */ KEY_DELETE,
  898. /* 76 */ KEY_F4,
  899. /* 77 */ KEY_END,
  900. /* 78 */ KEY_F2,
  901. /* 79 */ KEY_PAGEDOWN,
  902. /* 7a */ KEY_F1,
  903. /* 7b */ KEY_LEFT,
  904. /* 7c */ KEY_RIGHT,
  905. /* 7d */ KEY_DOWN,
  906. /* 7e */ KEY_UP,
  907. /* 7f */ KEY_UNKNOWN,
  908. };
  909. if (key >= 128)
  910. return KEY_UNKNOWN;
  911. return table[key];
  912. }
  913. struct _KeyCodeMap {
  914. UniChar kchar;
  915. int kcode;
  916. };
  917. static const _KeyCodeMap _keycodes[55] = {
  918. { '`', KEY_QUOTELEFT },
  919. { '~', KEY_ASCIITILDE },
  920. { '0', KEY_0 },
  921. { '1', KEY_1 },
  922. { '2', KEY_2 },
  923. { '3', KEY_3 },
  924. { '4', KEY_4 },
  925. { '5', KEY_5 },
  926. { '6', KEY_6 },
  927. { '7', KEY_7 },
  928. { '8', KEY_8 },
  929. { '9', KEY_9 },
  930. { '-', KEY_MINUS },
  931. { '_', KEY_UNDERSCORE },
  932. { '=', KEY_EQUAL },
  933. { '+', KEY_PLUS },
  934. { 'q', KEY_Q },
  935. { 'w', KEY_W },
  936. { 'e', KEY_E },
  937. { 'r', KEY_R },
  938. { 't', KEY_T },
  939. { 'y', KEY_Y },
  940. { 'u', KEY_U },
  941. { 'i', KEY_I },
  942. { 'o', KEY_O },
  943. { 'p', KEY_P },
  944. { '[', KEY_BRACELEFT },
  945. { ']', KEY_BRACERIGHT },
  946. { '{', KEY_BRACELEFT },
  947. { '}', KEY_BRACERIGHT },
  948. { 'a', KEY_A },
  949. { 's', KEY_S },
  950. { 'd', KEY_D },
  951. { 'f', KEY_F },
  952. { 'g', KEY_G },
  953. { 'h', KEY_H },
  954. { 'j', KEY_J },
  955. { 'k', KEY_K },
  956. { 'l', KEY_L },
  957. { ';', KEY_SEMICOLON },
  958. { ':', KEY_COLON },
  959. { '\'', KEY_APOSTROPHE },
  960. { '\"', KEY_QUOTEDBL },
  961. { '\\', KEY_BACKSLASH },
  962. { '#', KEY_NUMBERSIGN },
  963. { 'z', KEY_Z },
  964. { 'x', KEY_X },
  965. { 'c', KEY_C },
  966. { 'v', KEY_V },
  967. { 'b', KEY_B },
  968. { 'n', KEY_N },
  969. { 'm', KEY_M },
  970. { ',', KEY_COMMA },
  971. { '.', KEY_PERIOD },
  972. { '/', KEY_SLASH }
  973. };
  974. static int remapKey(unsigned int key, unsigned int state) {
  975. if (isNumpadKey(key))
  976. return translateKey(key);
  977. TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
  978. if (!currentKeyboard)
  979. return translateKey(key);
  980. CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
  981. if (!layoutData)
  982. return translateKey(key);
  983. const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
  984. UInt32 keysDown = 0;
  985. UniChar chars[4];
  986. UniCharCount realLength;
  987. OSStatus err = UCKeyTranslate(keyboardLayout,
  988. key,
  989. kUCKeyActionDisplay,
  990. (state >> 8) & 0xFF,
  991. LMGetKbdType(),
  992. kUCKeyTranslateNoDeadKeysBit,
  993. &keysDown,
  994. sizeof(chars) / sizeof(chars[0]),
  995. &realLength,
  996. chars);
  997. if (err != noErr) {
  998. return translateKey(key);
  999. }
  1000. for (unsigned int i = 0; i < 55; i++) {
  1001. if (_keycodes[i].kchar == chars[0]) {
  1002. return _keycodes[i].kcode;
  1003. }
  1004. }
  1005. return translateKey(key);
  1006. }
  1007. - (void)keyDown:(NSEvent *)event {
  1008. ignore_momentum_scroll = true;
  1009. // Ignore all input if IME input is in progress
  1010. if (!imeInputEventInProgress) {
  1011. NSString *characters = [event characters];
  1012. NSUInteger length = [characters length];
  1013. if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
  1014. // Fallback unicode character handler used if IME is not active
  1015. for (NSUInteger i = 0; i < length; i++) {
  1016. OS_OSX::KeyEvent ke;
  1017. ke.osx_state = [event modifierFlags];
  1018. ke.pressed = true;
  1019. ke.echo = [event isARepeat];
  1020. ke.scancode = remapKey([event keyCode], [event modifierFlags]);
  1021. ke.physical_scancode = translateKey([event keyCode]);
  1022. ke.raw = true;
  1023. ke.unicode = [characters characterAtIndex:i];
  1024. push_to_key_event_buffer(ke);
  1025. }
  1026. } else {
  1027. OS_OSX::KeyEvent ke;
  1028. ke.osx_state = [event modifierFlags];
  1029. ke.pressed = true;
  1030. ke.echo = [event isARepeat];
  1031. ke.scancode = remapKey([event keyCode], [event modifierFlags]);
  1032. ke.physical_scancode = translateKey([event keyCode]);
  1033. ke.raw = false;
  1034. ke.unicode = 0;
  1035. push_to_key_event_buffer(ke);
  1036. }
  1037. }
  1038. // Pass events to IME handler
  1039. if (OS_OSX::singleton->im_active)
  1040. [self interpretKeyEvents:[NSArray arrayWithObject:event]];
  1041. }
  1042. - (void)flagsChanged:(NSEvent *)event {
  1043. ignore_momentum_scroll = true;
  1044. // Ignore all input if IME input is in progress
  1045. if (!imeInputEventInProgress) {
  1046. OS_OSX::KeyEvent ke;
  1047. ke.echo = false;
  1048. ke.raw = true;
  1049. int key = [event keyCode];
  1050. int mod = [event modifierFlags];
  1051. if (key == 0x36 || key == 0x37) {
  1052. if (mod & NSEventModifierFlagCommand) {
  1053. mod &= ~NSEventModifierFlagCommand;
  1054. ke.pressed = true;
  1055. } else {
  1056. ke.pressed = false;
  1057. }
  1058. } else if (key == 0x38 || key == 0x3c) {
  1059. if (mod & NSEventModifierFlagShift) {
  1060. mod &= ~NSEventModifierFlagShift;
  1061. ke.pressed = true;
  1062. } else {
  1063. ke.pressed = false;
  1064. }
  1065. } else if (key == 0x3a || key == 0x3d) {
  1066. if (mod & NSEventModifierFlagOption) {
  1067. mod &= ~NSEventModifierFlagOption;
  1068. ke.pressed = true;
  1069. } else {
  1070. ke.pressed = false;
  1071. }
  1072. } else if (key == 0x3b || key == 0x3e) {
  1073. if (mod & NSEventModifierFlagControl) {
  1074. mod &= ~NSEventModifierFlagControl;
  1075. ke.pressed = true;
  1076. } else {
  1077. ke.pressed = false;
  1078. }
  1079. } else {
  1080. return;
  1081. }
  1082. ke.osx_state = mod;
  1083. ke.scancode = remapKey(key, mod);
  1084. ke.physical_scancode = translateKey(key);
  1085. ke.unicode = 0;
  1086. push_to_key_event_buffer(ke);
  1087. }
  1088. }
  1089. - (void)keyUp:(NSEvent *)event {
  1090. // Ignore all input if IME input is in progress
  1091. if (!imeInputEventInProgress) {
  1092. NSString *characters = [event characters];
  1093. NSUInteger length = [characters length];
  1094. // Fallback unicode character handler used if IME is not active
  1095. if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
  1096. for (NSUInteger i = 0; i < length; i++) {
  1097. OS_OSX::KeyEvent ke;
  1098. ke.osx_state = [event modifierFlags];
  1099. ke.pressed = false;
  1100. ke.echo = [event isARepeat];
  1101. ke.scancode = remapKey([event keyCode], [event modifierFlags]);
  1102. ke.physical_scancode = translateKey([event keyCode]);
  1103. ke.raw = true;
  1104. ke.unicode = [characters characterAtIndex:i];
  1105. push_to_key_event_buffer(ke);
  1106. }
  1107. } else {
  1108. OS_OSX::KeyEvent ke;
  1109. ke.osx_state = [event modifierFlags];
  1110. ke.pressed = false;
  1111. ke.echo = [event isARepeat];
  1112. ke.scancode = remapKey([event keyCode], [event modifierFlags]);
  1113. ke.physical_scancode = translateKey([event keyCode]);
  1114. ke.raw = true;
  1115. ke.unicode = 0;
  1116. push_to_key_event_buffer(ke);
  1117. }
  1118. }
  1119. }
  1120. inline void sendScrollEvent(int button, double factor, int modifierFlags) {
  1121. unsigned int mask = 1 << (button - 1);
  1122. Vector2 mouse_pos = Vector2(mouse_x, mouse_y);
  1123. Ref<InputEventMouseButton> sc;
  1124. sc.instance();
  1125. get_key_modifier_state(modifierFlags, sc);
  1126. sc->set_button_index(button);
  1127. sc->set_factor(factor);
  1128. sc->set_pressed(true);
  1129. sc->set_position(mouse_pos);
  1130. sc->set_global_position(mouse_pos);
  1131. button_mask |= mask;
  1132. sc->set_button_mask(button_mask);
  1133. OS_OSX::singleton->push_input(sc);
  1134. sc.instance();
  1135. sc->set_button_index(button);
  1136. sc->set_factor(factor);
  1137. sc->set_pressed(false);
  1138. sc->set_position(mouse_pos);
  1139. sc->set_global_position(mouse_pos);
  1140. button_mask &= ~mask;
  1141. sc->set_button_mask(button_mask);
  1142. OS_OSX::singleton->push_input(sc);
  1143. }
  1144. inline void sendPanEvent(double dx, double dy, int modifierFlags) {
  1145. Ref<InputEventPanGesture> pg;
  1146. pg.instance();
  1147. get_key_modifier_state(modifierFlags, pg);
  1148. Vector2 mouse_pos = Vector2(mouse_x, mouse_y);
  1149. pg->set_position(mouse_pos);
  1150. pg->set_delta(Vector2(-dx, -dy));
  1151. OS_OSX::singleton->push_input(pg);
  1152. }
  1153. - (void)scrollWheel:(NSEvent *)event {
  1154. double deltaX, deltaY;
  1155. get_mouse_pos([event locationInWindow]);
  1156. deltaX = [event scrollingDeltaX];
  1157. deltaY = [event scrollingDeltaY];
  1158. if ([event hasPreciseScrollingDeltas]) {
  1159. deltaX *= 0.03;
  1160. deltaY *= 0.03;
  1161. }
  1162. if ([event momentumPhase] != NSEventPhaseNone) {
  1163. if (ignore_momentum_scroll) {
  1164. return;
  1165. }
  1166. } else {
  1167. ignore_momentum_scroll = false;
  1168. }
  1169. if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) {
  1170. sendPanEvent(deltaX, deltaY, [event modifierFlags]);
  1171. } else {
  1172. if (fabs(deltaX)) {
  1173. sendScrollEvent(0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]);
  1174. }
  1175. if (fabs(deltaY)) {
  1176. sendScrollEvent(0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]);
  1177. }
  1178. }
  1179. }
  1180. @end
  1181. @interface GodotWindow : NSWindow {
  1182. }
  1183. @end
  1184. @implementation GodotWindow
  1185. - (BOOL)canBecomeKeyWindow {
  1186. // Required for NSBorderlessWindowMask windows
  1187. return YES;
  1188. }
  1189. @end
  1190. void OS_OSX::_update_global_menu() {
  1191. NSMenu *main_menu = [NSApp mainMenu];
  1192. for (int i = [main_menu numberOfItems] - 1; i > 0; i--) {
  1193. [main_menu removeItemAtIndex:i];
  1194. }
  1195. for (List<String>::Element *E = global_menus_order.front(); E; E = E->next()) {
  1196. Vector<GlobalMenuItem> &items = global_menus[E->get()];
  1197. NSMenu *menu = [[[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:E->get().utf8().get_data()]] autorelease];
  1198. for (int i = 0; i < items.size(); i++) {
  1199. if (items[i].label == String()) {
  1200. [menu addItem:[NSMenuItem separatorItem]];
  1201. } else {
  1202. NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:items[i].label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
  1203. [menu_item setRepresentedObject:[NSValue valueWithPointer:&(items[i])]];
  1204. }
  1205. }
  1206. NSMenuItem *menu_item = [main_menu addItemWithTitle:[NSString stringWithUTF8String:E->get().utf8().get_data()] action:nil keyEquivalent:@""];
  1207. [main_menu setSubmenu:menu forItem:menu_item];
  1208. }
  1209. }
  1210. void OS_OSX::global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta) {
  1211. if (!global_menus.has(p_menu) && (p_menu != "_dock")) {
  1212. global_menus_order.push_back(p_menu);
  1213. }
  1214. global_menus[p_menu].push_back(GlobalMenuItem(p_label, p_signal, p_meta));
  1215. _update_global_menu();
  1216. }
  1217. void OS_OSX::global_menu_add_separator(const String &p_menu) {
  1218. if (!global_menus.has(p_menu) && (p_menu != "_dock")) {
  1219. global_menus_order.push_back(p_menu);
  1220. }
  1221. global_menus[p_menu].push_back(GlobalMenuItem());
  1222. _update_global_menu();
  1223. }
  1224. void OS_OSX::global_menu_remove_item(const String &p_menu, int p_idx) {
  1225. ERR_FAIL_INDEX(p_idx, global_menus[p_menu].size());
  1226. global_menus[p_menu].remove(p_idx);
  1227. _update_global_menu();
  1228. }
  1229. void OS_OSX::global_menu_clear(const String &p_menu) {
  1230. if (global_menus.has(p_menu)) {
  1231. global_menus[p_menu].clear();
  1232. if (p_menu != "_dock") {
  1233. global_menus.erase(p_menu);
  1234. global_menus_order.erase(p_menu);
  1235. }
  1236. }
  1237. _update_global_menu();
  1238. }
  1239. Point2 OS_OSX::get_ime_selection() const {
  1240. return im_selection;
  1241. }
  1242. String OS_OSX::get_ime_text() const {
  1243. return im_text;
  1244. }
  1245. String OS_OSX::get_unique_id() const {
  1246. static String serial_number;
  1247. if (serial_number.empty()) {
  1248. io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
  1249. CFStringRef serialNumberAsCFString = NULL;
  1250. if (platformExpert) {
  1251. serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
  1252. IOObjectRelease(platformExpert);
  1253. }
  1254. NSString *serialNumberAsNSString = nil;
  1255. if (serialNumberAsCFString) {
  1256. serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
  1257. CFRelease(serialNumberAsCFString);
  1258. }
  1259. serial_number = [serialNumberAsNSString UTF8String];
  1260. }
  1261. return serial_number;
  1262. }
  1263. void OS_OSX::set_ime_active(const bool p_active) {
  1264. im_active = p_active;
  1265. if (!im_active)
  1266. [window_view cancelComposition];
  1267. }
  1268. void OS_OSX::set_ime_position(const Point2 &p_pos) {
  1269. im_position = p_pos;
  1270. }
  1271. void OS_OSX::initialize_core() {
  1272. crash_handler.initialize();
  1273. OS_Unix::initialize_core();
  1274. DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
  1275. DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA);
  1276. DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
  1277. }
  1278. struct LayoutInfo {
  1279. String name;
  1280. String code;
  1281. };
  1282. static Vector<LayoutInfo> kbd_layouts;
  1283. static int current_layout = 0;
  1284. static bool keyboard_layout_dirty = true;
  1285. static OS::LatinKeyboardVariant latin_variant = OS::LATIN_KEYBOARD_QWERTY;
  1286. static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
  1287. kbd_layouts.clear();
  1288. current_layout = 0;
  1289. keyboard_layout_dirty = true;
  1290. }
  1291. static bool displays_arrangement_dirty = true;
  1292. static bool displays_scale_dirty = true;
  1293. static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
  1294. displays_arrangement_dirty = true;
  1295. displays_scale_dirty = true;
  1296. }
  1297. int OS_OSX::get_current_video_driver() const {
  1298. return video_driver_index;
  1299. }
  1300. Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
  1301. /*** OSX INITIALIZATION ***/
  1302. /*** OSX INITIALIZATION ***/
  1303. /*** OSX INITIALIZATION ***/
  1304. keyboard_layout_dirty = true;
  1305. displays_arrangement_dirty = true;
  1306. displays_scale_dirty = true;
  1307. // Register to be notified on keyboard layout changes
  1308. CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
  1309. NULL, keyboard_layout_changed,
  1310. kTISNotifySelectedKeyboardInputSourceChanged, NULL,
  1311. CFNotificationSuspensionBehaviorDeliverImmediately);
  1312. // Register to be notified on displays arrangement changes
  1313. CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL);
  1314. window_delegate = [[GodotWindowDelegate alloc] init];
  1315. // Don't use accumulation buffer support; it's not accelerated
  1316. // Aux buffers probably aren't accelerated either
  1317. unsigned int styleMask;
  1318. if (p_desired.borderless_window) {
  1319. styleMask = NSWindowStyleMaskBorderless;
  1320. } else {
  1321. resizable = p_desired.resizable;
  1322. styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (p_desired.resizable ? NSWindowStyleMaskResizable : 0);
  1323. }
  1324. float displayScale = get_screen_max_scale();
  1325. window_object = [[GodotWindow alloc]
  1326. initWithContentRect:NSMakeRect(0, 0, p_desired.width / displayScale, p_desired.height / displayScale)
  1327. styleMask:styleMask
  1328. backing:NSBackingStoreBuffered
  1329. defer:NO];
  1330. ERR_FAIL_COND_V(window_object == nil, ERR_UNAVAILABLE);
  1331. window_view = [[GodotContentView alloc] init];
  1332. if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_14) {
  1333. [window_view setWantsLayer:TRUE];
  1334. }
  1335. window_size.width = p_desired.width;
  1336. window_size.height = p_desired.height;
  1337. if (displayScale > 1.0) {
  1338. [window_view setWantsBestResolutionOpenGLSurface:YES];
  1339. //if (current_videomode.resizable)
  1340. [window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  1341. } else {
  1342. [window_view setWantsBestResolutionOpenGLSurface:NO];
  1343. }
  1344. //[window_object setTitle:[NSString stringWithUTF8String:"GodotEnginies"]];
  1345. [window_object setContentView:window_view];
  1346. [window_object setDelegate:window_delegate];
  1347. if (!is_no_window_mode_enabled()) {
  1348. [window_object setAcceptsMouseMovedEvents:YES];
  1349. [((NSWindow *)window_object) center];
  1350. }
  1351. [window_object setRestorable:NO];
  1352. unsigned int attributeCount = 0;
  1353. // OS X needs non-zero color size, so set reasonable values
  1354. int colorBits = 32;
  1355. // Fail if a robustness strategy was requested
  1356. #define ADD_ATTR(x) \
  1357. { attributes[attributeCount++] = x; }
  1358. #define ADD_ATTR2(x, y) \
  1359. { \
  1360. ADD_ATTR(x); \
  1361. ADD_ATTR(y); \
  1362. }
  1363. // Arbitrary array size here
  1364. NSOpenGLPixelFormatAttribute attributes[40];
  1365. ADD_ATTR(NSOpenGLPFADoubleBuffer);
  1366. ADD_ATTR(NSOpenGLPFAClosestPolicy);
  1367. if (p_video_driver == VIDEO_DRIVER_GLES2) {
  1368. ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy);
  1369. } else {
  1370. //we now need OpenGL 3 or better, maybe even change this to 3_3Core ?
  1371. ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
  1372. }
  1373. ADD_ATTR2(NSOpenGLPFAColorSize, colorBits);
  1374. /*
  1375. if (fbconfig->alphaBits > 0)
  1376. ADD_ATTR2(NSOpenGLPFAAlphaSize, fbconfig->alphaBits);
  1377. */
  1378. ADD_ATTR2(NSOpenGLPFADepthSize, 24);
  1379. ADD_ATTR2(NSOpenGLPFAStencilSize, 8);
  1380. /*
  1381. if (fbconfig->stereo)
  1382. ADD_ATTR(NSOpenGLPFAStereo);
  1383. */
  1384. /*
  1385. if (fbconfig->samples > 0) {
  1386. ADD_ATTR2(NSOpenGLPFASampleBuffers, 1);
  1387. ADD_ATTR2(NSOpenGLPFASamples, fbconfig->samples);
  1388. }
  1389. */
  1390. // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB
  1391. // framebuffer, so there's no need (and no way) to request it
  1392. ADD_ATTR(0);
  1393. #undef ADD_ATTR
  1394. #undef ADD_ATTR2
  1395. pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
  1396. ERR_FAIL_COND_V(pixelFormat == nil, ERR_UNAVAILABLE);
  1397. context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
  1398. ERR_FAIL_COND_V(context == nil, ERR_UNAVAILABLE);
  1399. [window_view setOpenGLContext:context];
  1400. [context makeCurrentContext];
  1401. GLint dim[2];
  1402. dim[0] = window_size.width;
  1403. dim[1] = window_size.height;
  1404. CGLSetParameter((CGLContextObj)[context CGLContextObj], kCGLCPSurfaceBackingSize, &dim[0]);
  1405. CGLEnable((CGLContextObj)[context CGLContextObj], kCGLCESurfaceBackingSize);
  1406. set_use_vsync(p_desired.use_vsync);
  1407. if (!is_no_window_mode_enabled()) {
  1408. [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
  1409. [NSApp activateIgnoringOtherApps:YES];
  1410. }
  1411. _update_window();
  1412. if (!is_no_window_mode_enabled()) {
  1413. [window_object makeKeyAndOrderFront:nil];
  1414. }
  1415. on_top = p_desired.always_on_top;
  1416. if (p_desired.always_on_top && !p_desired.fullscreen) {
  1417. [window_object setLevel:NSFloatingWindowLevel];
  1418. }
  1419. if (p_desired.fullscreen)
  1420. zoomed = true;
  1421. /*** END OSX INITIALIZATION ***/
  1422. bool gles3 = true;
  1423. if (p_video_driver == VIDEO_DRIVER_GLES2) {
  1424. gles3 = false;
  1425. }
  1426. bool editor = Engine::get_singleton()->is_editor_hint();
  1427. bool gl_initialization_error = false;
  1428. while (true) {
  1429. if (gles3) {
  1430. if (RasterizerGLES3::is_viable() == OK) {
  1431. RasterizerGLES3::register_config();
  1432. RasterizerGLES3::make_current();
  1433. break;
  1434. } else {
  1435. if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2") || editor) {
  1436. p_video_driver = VIDEO_DRIVER_GLES2;
  1437. gles3 = false;
  1438. continue;
  1439. } else {
  1440. gl_initialization_error = true;
  1441. break;
  1442. }
  1443. }
  1444. } else {
  1445. if (RasterizerGLES2::is_viable() == OK) {
  1446. RasterizerGLES2::register_config();
  1447. RasterizerGLES2::make_current();
  1448. break;
  1449. } else {
  1450. gl_initialization_error = true;
  1451. break;
  1452. }
  1453. }
  1454. }
  1455. if (gl_initialization_error) {
  1456. OS::get_singleton()->alert("Your video card driver does not support any of the supported OpenGL versions.\n"
  1457. "Please update your drivers or if you have a very old or integrated GPU upgrade it.",
  1458. "Unable to initialize Video driver");
  1459. return ERR_UNAVAILABLE;
  1460. }
  1461. video_driver_index = p_video_driver;
  1462. visual_server = memnew(VisualServerRaster);
  1463. if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
  1464. visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
  1465. }
  1466. visual_server->init();
  1467. AudioDriverManager::initialize(p_audio_driver);
  1468. input = memnew(InputDefault);
  1469. joypad_osx = memnew(JoypadOSX);
  1470. power_manager = memnew(PowerOSX);
  1471. restore_rect = Rect2(get_window_position(), get_window_size());
  1472. if (p_desired.layered) {
  1473. set_window_per_pixel_transparency_enabled(true);
  1474. }
  1475. update_real_mouse_position();
  1476. if (is_no_window_mode_enabled()) {
  1477. [NSApp hide:nil];
  1478. }
  1479. return OK;
  1480. }
  1481. void OS_OSX::finalize() {
  1482. #ifdef COREMIDI_ENABLED
  1483. midi_driver.close();
  1484. #endif
  1485. CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
  1486. CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
  1487. delete_main_loop();
  1488. memdelete(joypad_osx);
  1489. memdelete(input);
  1490. cursors_cache.clear();
  1491. visual_server->finish();
  1492. memdelete(visual_server);
  1493. //memdelete(rasterizer);
  1494. }
  1495. void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
  1496. main_loop = p_main_loop;
  1497. input->set_main_loop(p_main_loop);
  1498. }
  1499. void OS_OSX::delete_main_loop() {
  1500. if (!main_loop)
  1501. return;
  1502. memdelete(main_loop);
  1503. main_loop = NULL;
  1504. }
  1505. String OS_OSX::get_name() const {
  1506. return "OSX";
  1507. }
  1508. #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
  1509. class OSXTerminalLogger : public StdLogger {
  1510. public:
  1511. virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR) {
  1512. if (!should_log(true)) {
  1513. return;
  1514. }
  1515. const char *err_details;
  1516. if (p_rationale && p_rationale[0])
  1517. err_details = p_rationale;
  1518. else
  1519. err_details = p_code;
  1520. switch (p_type) {
  1521. case ERR_WARNING:
  1522. if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
  1523. os_log_info(OS_LOG_DEFAULT,
  1524. "WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.",
  1525. p_function, err_details, p_file, p_line);
  1526. }
  1527. logf_error("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function,
  1528. err_details);
  1529. logf_error("\E[0;33m At: %s:%i.\E[0m\n", p_file, p_line);
  1530. break;
  1531. case ERR_SCRIPT:
  1532. if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
  1533. os_log_error(OS_LOG_DEFAULT,
  1534. "SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
  1535. p_function, err_details, p_file, p_line);
  1536. }
  1537. logf_error("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function,
  1538. err_details);
  1539. logf_error("\E[0;35m At: %s:%i.\E[0m\n", p_file, p_line);
  1540. break;
  1541. case ERR_SHADER:
  1542. if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
  1543. os_log_error(OS_LOG_DEFAULT,
  1544. "SHADER ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
  1545. p_function, err_details, p_file, p_line);
  1546. }
  1547. logf_error("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n", p_function,
  1548. err_details);
  1549. logf_error("\E[0;36m At: %s:%i.\E[0m\n", p_file, p_line);
  1550. break;
  1551. case ERR_ERROR:
  1552. default:
  1553. if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
  1554. os_log_error(OS_LOG_DEFAULT,
  1555. "ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
  1556. p_function, err_details, p_file, p_line);
  1557. }
  1558. logf_error("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
  1559. logf_error("\E[0;31m At: %s:%i.\E[0m\n", p_file, p_line);
  1560. break;
  1561. }
  1562. }
  1563. };
  1564. #else
  1565. typedef UnixTerminalLogger OSXTerminalLogger;
  1566. #endif
  1567. void OS_OSX::alert(const String &p_alert, const String &p_title) {
  1568. if (is_no_window_mode_enabled()) {
  1569. print_line("ALERT: " + p_title + ": " + p_alert);
  1570. return;
  1571. }
  1572. // Set OS X-compliant variables
  1573. NSAlert *window = [[NSAlert alloc] init];
  1574. NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
  1575. NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
  1576. [window addButtonWithTitle:@"OK"];
  1577. [window setMessageText:ns_title];
  1578. [window setInformativeText:ns_alert];
  1579. [window setAlertStyle:NSAlertStyleWarning];
  1580. // Display it, then release
  1581. id key_window = [[NSApplication sharedApplication] keyWindow];
  1582. [window runModal];
  1583. [window release];
  1584. if (key_window) {
  1585. [key_window makeKeyAndOrderFront:nil];
  1586. }
  1587. }
  1588. Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
  1589. String path = p_path;
  1590. if (!FileAccess::exists(path)) {
  1591. //this code exists so gdnative can load .dylib files from within the executable path
  1592. path = get_executable_path().get_base_dir().plus_file(p_path.get_file());
  1593. }
  1594. if (!FileAccess::exists(path)) {
  1595. //this code exists so gdnative can load .dylib files from a standard macOS location
  1596. path = get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file());
  1597. }
  1598. p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
  1599. ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
  1600. return OK;
  1601. }
  1602. void OS_OSX::set_cursor_shape(CursorShape p_shape) {
  1603. if (cursor_shape == p_shape)
  1604. return;
  1605. if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
  1606. cursor_shape = p_shape;
  1607. return;
  1608. }
  1609. if (cursors[p_shape] != NULL) {
  1610. [cursors[p_shape] set];
  1611. } else {
  1612. switch (p_shape) {
  1613. case CURSOR_ARROW:
  1614. [[NSCursor arrowCursor] set];
  1615. break;
  1616. case CURSOR_IBEAM:
  1617. [[NSCursor IBeamCursor] set];
  1618. break;
  1619. case CURSOR_POINTING_HAND:
  1620. [[NSCursor pointingHandCursor] set];
  1621. break;
  1622. case CURSOR_CROSS:
  1623. [[NSCursor crosshairCursor] set];
  1624. break;
  1625. case CURSOR_WAIT:
  1626. [[NSCursor arrowCursor] set];
  1627. break;
  1628. case CURSOR_BUSY:
  1629. [[NSCursor arrowCursor] set];
  1630. break;
  1631. case CURSOR_DRAG:
  1632. [[NSCursor closedHandCursor] set];
  1633. break;
  1634. case CURSOR_CAN_DROP:
  1635. [[NSCursor openHandCursor] set];
  1636. break;
  1637. case CURSOR_FORBIDDEN:
  1638. [[NSCursor operationNotAllowedCursor] set];
  1639. break;
  1640. case CURSOR_VSIZE:
  1641. [cursorFromSelector(@selector(_windowResizeNorthSouthCursor), @selector(resizeUpDownCursor)) set];
  1642. break;
  1643. case CURSOR_HSIZE:
  1644. [cursorFromSelector(@selector(_windowResizeEastWestCursor), @selector(resizeLeftRightCursor)) set];
  1645. break;
  1646. case CURSOR_BDIAGSIZE:
  1647. [cursorFromSelector(@selector(_windowResizeNorthEastSouthWestCursor)) set];
  1648. break;
  1649. case CURSOR_FDIAGSIZE:
  1650. [cursorFromSelector(@selector(_windowResizeNorthWestSouthEastCursor)) set];
  1651. break;
  1652. case CURSOR_MOVE:
  1653. [[NSCursor arrowCursor] set];
  1654. break;
  1655. case CURSOR_VSPLIT:
  1656. [[NSCursor resizeUpDownCursor] set];
  1657. break;
  1658. case CURSOR_HSPLIT:
  1659. [[NSCursor resizeLeftRightCursor] set];
  1660. break;
  1661. case CURSOR_HELP:
  1662. [cursorFromSelector(@selector(_helpCursor)) set];
  1663. break;
  1664. default: {
  1665. };
  1666. }
  1667. }
  1668. cursor_shape = p_shape;
  1669. }
  1670. OS::CursorShape OS_OSX::get_cursor_shape() const {
  1671. return cursor_shape;
  1672. }
  1673. void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
  1674. if (p_cursor.is_valid()) {
  1675. Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
  1676. if (cursor_c) {
  1677. if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
  1678. set_cursor_shape(p_shape);
  1679. return;
  1680. }
  1681. cursors_cache.erase(p_shape);
  1682. }
  1683. Ref<Texture> texture = p_cursor;
  1684. Ref<AtlasTexture> atlas_texture = p_cursor;
  1685. Ref<Image> image;
  1686. Size2 texture_size;
  1687. Rect2 atlas_rect;
  1688. if (texture.is_valid()) {
  1689. image = texture->get_data();
  1690. }
  1691. if (!image.is_valid() && atlas_texture.is_valid()) {
  1692. texture = atlas_texture->get_atlas();
  1693. atlas_rect.size.width = texture->get_width();
  1694. atlas_rect.size.height = texture->get_height();
  1695. atlas_rect.position.x = atlas_texture->get_region().position.x;
  1696. atlas_rect.position.y = atlas_texture->get_region().position.y;
  1697. texture_size.width = atlas_texture->get_region().size.x;
  1698. texture_size.height = atlas_texture->get_region().size.y;
  1699. } else if (image.is_valid()) {
  1700. texture_size.width = texture->get_width();
  1701. texture_size.height = texture->get_height();
  1702. }
  1703. ERR_FAIL_COND(!texture.is_valid());
  1704. ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
  1705. ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
  1706. ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
  1707. image = texture->get_data();
  1708. ERR_FAIL_COND(!image.is_valid());
  1709. NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
  1710. initWithBitmapDataPlanes:NULL
  1711. pixelsWide:int(texture_size.width)
  1712. pixelsHigh:int(texture_size.height)
  1713. bitsPerSample:8
  1714. samplesPerPixel:4
  1715. hasAlpha:YES
  1716. isPlanar:NO
  1717. colorSpaceName:NSDeviceRGBColorSpace
  1718. bytesPerRow:int(texture_size.width) * 4
  1719. bitsPerPixel:32];
  1720. ERR_FAIL_COND(imgrep == nil);
  1721. uint8_t *pixels = [imgrep bitmapData];
  1722. int len = int(texture_size.width * texture_size.height);
  1723. PoolVector<uint8_t> data = image->get_data();
  1724. PoolVector<uint8_t>::Read r = data.read();
  1725. image->lock();
  1726. /* Premultiply the alpha channel */
  1727. for (int i = 0; i < len; i++) {
  1728. int row_index = floor(i / texture_size.width) + atlas_rect.position.y;
  1729. int column_index = (i % int(texture_size.width)) + atlas_rect.position.x;
  1730. if (atlas_texture.is_valid()) {
  1731. column_index = MIN(column_index, atlas_rect.size.width - 1);
  1732. row_index = MIN(row_index, atlas_rect.size.height - 1);
  1733. }
  1734. uint32_t color = image->get_pixel(column_index, row_index).to_argb32();
  1735. uint8_t alpha = (color >> 24) & 0xFF;
  1736. pixels[i * 4 + 0] = ((color >> 16) & 0xFF) * alpha / 255;
  1737. pixels[i * 4 + 1] = ((color >> 8) & 0xFF) * alpha / 255;
  1738. pixels[i * 4 + 2] = ((color)&0xFF) * alpha / 255;
  1739. pixels[i * 4 + 3] = alpha;
  1740. }
  1741. image->unlock();
  1742. NSImage *nsimage = [[NSImage alloc] initWithSize:NSMakeSize(texture_size.width, texture_size.height)];
  1743. [nsimage addRepresentation:imgrep];
  1744. NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)];
  1745. [cursors[p_shape] release];
  1746. cursors[p_shape] = cursor;
  1747. Vector<Variant> params;
  1748. params.push_back(p_cursor);
  1749. params.push_back(p_hotspot);
  1750. cursors_cache.insert(p_shape, params);
  1751. if (p_shape == cursor_shape) {
  1752. if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
  1753. [cursor set];
  1754. }
  1755. }
  1756. [imgrep release];
  1757. [nsimage release];
  1758. } else {
  1759. // Reset to default system cursor
  1760. if (cursors[p_shape] != NULL) {
  1761. [cursors[p_shape] release];
  1762. cursors[p_shape] = NULL;
  1763. }
  1764. CursorShape c = cursor_shape;
  1765. cursor_shape = CURSOR_MAX;
  1766. set_cursor_shape(c);
  1767. cursors_cache.erase(p_shape);
  1768. }
  1769. }
  1770. void OS_OSX::set_mouse_show(bool p_show) {
  1771. }
  1772. void OS_OSX::set_mouse_grab(bool p_grab) {
  1773. }
  1774. bool OS_OSX::is_mouse_grab_enabled() const {
  1775. return mouse_grab;
  1776. }
  1777. void OS_OSX::warp_mouse_position(const Point2 &p_to) {
  1778. //copied from windows impl with osx native calls
  1779. if (mouse_mode == MOUSE_MODE_CAPTURED) {
  1780. mouse_x = p_to.x;
  1781. mouse_y = p_to.y;
  1782. } else { //set OS position
  1783. //local point in window coords
  1784. const NSRect contentRect = [window_view frame];
  1785. float displayScale = get_screen_max_scale();
  1786. NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0);
  1787. NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin;
  1788. //point in scren coords
  1789. CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
  1790. //do the warping
  1791. CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
  1792. CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
  1793. CGAssociateMouseAndMouseCursorPosition(false);
  1794. CGWarpMouseCursorPosition(lMouseWarpPos);
  1795. if (mouse_mode != MOUSE_MODE_CONFINED) {
  1796. CGAssociateMouseAndMouseCursorPosition(true);
  1797. }
  1798. }
  1799. }
  1800. void OS_OSX::update_real_mouse_position() {
  1801. get_mouse_pos([window_object mouseLocationOutsideOfEventStream]);
  1802. input->set_mouse_position(Point2(mouse_x, mouse_y));
  1803. }
  1804. Point2 OS_OSX::get_mouse_position() const {
  1805. return Vector2(mouse_x, mouse_y);
  1806. }
  1807. int OS_OSX::get_mouse_button_state() const {
  1808. return button_mask;
  1809. }
  1810. void OS_OSX::set_window_title(const String &p_title) {
  1811. title = p_title;
  1812. [window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
  1813. }
  1814. void OS_OSX::set_window_mouse_passthrough(const PoolVector2Array &p_region) {
  1815. mpath.clear();
  1816. for (int i = 0; i < p_region.size(); i++) {
  1817. mpath.push_back(p_region[i]);
  1818. }
  1819. }
  1820. void OS_OSX::set_native_icon(const String &p_filename) {
  1821. FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
  1822. ERR_FAIL_COND(!f);
  1823. Vector<uint8_t> data;
  1824. uint64_t len = f->get_len();
  1825. data.resize(len);
  1826. f->get_buffer((uint8_t *)&data.write[0], len);
  1827. memdelete(f);
  1828. NSData *icon_data = [[[NSData alloc] initWithBytes:&data.write[0] length:len] autorelease];
  1829. ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data.");
  1830. NSImage *icon = [[[NSImage alloc] initWithData:icon_data] autorelease];
  1831. ERR_FAIL_COND_MSG(!icon, "Error loading icon.");
  1832. [NSApp setApplicationIconImage:icon];
  1833. }
  1834. void OS_OSX::set_icon(const Ref<Image> &p_icon) {
  1835. Ref<Image> img = p_icon;
  1836. img = img->duplicate();
  1837. img->convert(Image::FORMAT_RGBA8);
  1838. NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc]
  1839. initWithBitmapDataPlanes:NULL
  1840. pixelsWide:img->get_width()
  1841. pixelsHigh:img->get_height()
  1842. bitsPerSample:8
  1843. samplesPerPixel:4
  1844. hasAlpha:YES
  1845. isPlanar:NO
  1846. colorSpaceName:NSDeviceRGBColorSpace
  1847. bytesPerRow:img->get_width() * 4
  1848. bitsPerPixel:32] autorelease];
  1849. ERR_FAIL_COND(imgrep == nil);
  1850. uint8_t *pixels = [imgrep bitmapData];
  1851. int len = img->get_width() * img->get_height();
  1852. PoolVector<uint8_t> data = img->get_data();
  1853. PoolVector<uint8_t>::Read r = data.read();
  1854. /* Premultiply the alpha channel */
  1855. for (int i = 0; i < len; i++) {
  1856. uint8_t alpha = r[i * 4 + 3];
  1857. pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
  1858. pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
  1859. pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
  1860. pixels[i * 4 + 3] = alpha;
  1861. }
  1862. NSImage *nsimg = [[[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())] autorelease];
  1863. ERR_FAIL_COND(nsimg == nil);
  1864. [nsimg addRepresentation:imgrep];
  1865. [NSApp setApplicationIconImage:nsimg];
  1866. }
  1867. MainLoop *OS_OSX::get_main_loop() const {
  1868. return main_loop;
  1869. }
  1870. String OS_OSX::get_config_path() const {
  1871. // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
  1872. if (has_environment("XDG_CONFIG_HOME")) {
  1873. if (get_environment("XDG_CONFIG_HOME").is_abs_path()) {
  1874. return get_environment("XDG_CONFIG_HOME");
  1875. } else {
  1876. WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Library/Application Support` or `.` per the XDG Base Directory specification.");
  1877. }
  1878. }
  1879. if (has_environment("HOME")) {
  1880. return get_environment("HOME").plus_file("Library/Application Support");
  1881. }
  1882. return ".";
  1883. }
  1884. String OS_OSX::get_data_path() const {
  1885. // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
  1886. if (has_environment("XDG_DATA_HOME")) {
  1887. if (get_environment("XDG_DATA_HOME").is_abs_path()) {
  1888. return get_environment("XDG_DATA_HOME");
  1889. } else {
  1890. WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification.");
  1891. }
  1892. }
  1893. return get_config_path();
  1894. }
  1895. String OS_OSX::get_cache_path() const {
  1896. // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
  1897. if (has_environment("XDG_CACHE_HOME")) {
  1898. if (get_environment("XDG_CACHE_HOME").is_abs_path()) {
  1899. return get_environment("XDG_CACHE_HOME");
  1900. } else {
  1901. WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Libary/Caches` or `get_config_path()` per the XDG Base Directory specification.");
  1902. }
  1903. }
  1904. if (has_environment("HOME")) {
  1905. return get_environment("HOME").plus_file("Library/Caches");
  1906. }
  1907. return get_config_path();
  1908. }
  1909. String OS_OSX::get_bundle_resource_dir() const {
  1910. NSBundle *main = [NSBundle mainBundle];
  1911. NSString *resourcePath = [main resourcePath];
  1912. char *utfs = strdup([resourcePath UTF8String]);
  1913. String ret;
  1914. ret.parse_utf8(utfs);
  1915. free(utfs);
  1916. return ret;
  1917. }
  1918. // Get properly capitalized engine name for system paths
  1919. String OS_OSX::get_godot_dir_name() const {
  1920. return String(VERSION_SHORT_NAME).capitalize();
  1921. }
  1922. String OS_OSX::get_system_dir(SystemDir p_dir) const {
  1923. NSSearchPathDirectory id;
  1924. bool found = true;
  1925. switch (p_dir) {
  1926. case SYSTEM_DIR_DESKTOP: {
  1927. id = NSDesktopDirectory;
  1928. } break;
  1929. case SYSTEM_DIR_DOCUMENTS: {
  1930. id = NSDocumentDirectory;
  1931. } break;
  1932. case SYSTEM_DIR_DOWNLOADS: {
  1933. id = NSDownloadsDirectory;
  1934. } break;
  1935. case SYSTEM_DIR_MOVIES: {
  1936. id = NSMoviesDirectory;
  1937. } break;
  1938. case SYSTEM_DIR_MUSIC: {
  1939. id = NSMusicDirectory;
  1940. } break;
  1941. case SYSTEM_DIR_PICTURES: {
  1942. id = NSPicturesDirectory;
  1943. } break;
  1944. default: {
  1945. found = false;
  1946. }
  1947. }
  1948. String ret;
  1949. if (found) {
  1950. NSArray *paths = NSSearchPathForDirectoriesInDomains(id, NSUserDomainMask, YES);
  1951. if (paths && [paths count] >= 1) {
  1952. char *utfs = strdup([[paths firstObject] UTF8String]);
  1953. ret.parse_utf8(utfs);
  1954. free(utfs);
  1955. }
  1956. }
  1957. return ret;
  1958. }
  1959. bool OS_OSX::can_draw() const {
  1960. return true;
  1961. }
  1962. void OS_OSX::set_clipboard(const String &p_text) {
  1963. NSString *copiedString = [NSString stringWithUTF8String:p_text.utf8().get_data()];
  1964. NSArray *copiedStringArray = [NSArray arrayWithObject:copiedString];
  1965. NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
  1966. [pasteboard clearContents];
  1967. [pasteboard writeObjects:copiedStringArray];
  1968. }
  1969. String OS_OSX::get_clipboard() const {
  1970. NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
  1971. NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
  1972. NSDictionary *options = [NSDictionary dictionary];
  1973. BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options];
  1974. if (!ok) {
  1975. return "";
  1976. }
  1977. NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
  1978. NSString *string = [objectsToPaste objectAtIndex:0];
  1979. char *utfs = strdup([string UTF8String]);
  1980. String ret;
  1981. ret.parse_utf8(utfs);
  1982. free(utfs);
  1983. return ret;
  1984. }
  1985. void OS_OSX::release_rendering_thread() {
  1986. [NSOpenGLContext clearCurrentContext];
  1987. }
  1988. void OS_OSX::make_rendering_thread() {
  1989. [context makeCurrentContext];
  1990. }
  1991. Error OS_OSX::shell_open(String p_uri) {
  1992. NSString *string = [NSString stringWithUTF8String:p_uri.utf8().get_data()];
  1993. NSURL *uri = [[NSURL alloc] initWithString:string];
  1994. // Escape special characters in filenames
  1995. if (!uri || !uri.scheme || [uri.scheme isEqual:@"file"]) {
  1996. uri = [[NSURL alloc] initWithString:[string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]];
  1997. }
  1998. [[NSWorkspace sharedWorkspace] openURL:uri];
  1999. return OK;
  2000. }
  2001. String OS_OSX::get_locale() const {
  2002. NSString *locale_code = [[NSLocale preferredLanguages] objectAtIndex:0];
  2003. return [locale_code UTF8String];
  2004. }
  2005. void OS_OSX::swap_buffers() {
  2006. [context flushBuffer];
  2007. }
  2008. void OS_OSX::wm_minimized(bool p_minimized) {
  2009. minimized = p_minimized;
  2010. };
  2011. void OS_OSX::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
  2012. }
  2013. OS::VideoMode OS_OSX::get_video_mode(int p_screen) const {
  2014. VideoMode vm;
  2015. vm.width = window_size.width;
  2016. vm.height = window_size.height;
  2017. return vm;
  2018. }
  2019. void OS_OSX::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
  2020. }
  2021. int OS_OSX::get_screen_count() const {
  2022. NSArray *screenArray = [NSScreen screens];
  2023. return [screenArray count];
  2024. };
  2025. // Returns the native top-left screen coordinate of the smallest rectangle
  2026. // that encompasses all screens. Needed in get_screen_position(),
  2027. // get_window_position, and set_window_position()
  2028. // to convert between OS X native screen coordinates and the ones expected by Godot
  2029. Point2 OS_OSX::get_screens_origin() const {
  2030. static Point2 origin;
  2031. if (displays_arrangement_dirty) {
  2032. origin = Point2();
  2033. for (int i = 0; i < get_screen_count(); i++) {
  2034. Point2 position = get_native_screen_position(i);
  2035. if (position.x < origin.x) {
  2036. origin.x = position.x;
  2037. }
  2038. if (position.y > origin.y) {
  2039. origin.y = position.y;
  2040. }
  2041. }
  2042. displays_arrangement_dirty = false;
  2043. }
  2044. return origin;
  2045. }
  2046. static int get_screen_index(NSScreen *screen) {
  2047. const NSUInteger index = [[NSScreen screens] indexOfObject:screen];
  2048. return index == NSNotFound ? 0 : index;
  2049. }
  2050. int OS_OSX::get_current_screen() const {
  2051. if (window_object) {
  2052. return get_screen_index([window_object screen]);
  2053. } else {
  2054. return get_screen_index([NSScreen mainScreen]);
  2055. }
  2056. };
  2057. void OS_OSX::set_current_screen(int p_screen) {
  2058. if (is_no_window_mode_enabled()) {
  2059. return;
  2060. }
  2061. Vector2 wpos = get_window_position() - get_screen_position(get_current_screen());
  2062. set_window_position(wpos + get_screen_position(p_screen));
  2063. };
  2064. Point2 OS_OSX::get_native_screen_position(int p_screen) const {
  2065. if (p_screen < 0) {
  2066. p_screen = get_current_screen();
  2067. }
  2068. NSArray *screenArray = [NSScreen screens];
  2069. if ((NSUInteger)p_screen < [screenArray count]) {
  2070. float display_scale = get_screen_max_scale();
  2071. NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
  2072. // Return the top-left corner of the screen, for OS X the y starts at the bottom
  2073. return Point2(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale;
  2074. }
  2075. return Point2();
  2076. }
  2077. Point2 OS_OSX::get_screen_position(int p_screen) const {
  2078. Point2 position = get_native_screen_position(p_screen) - get_screens_origin();
  2079. // OS X native y-coordinate relative to get_screens_origin() is negative,
  2080. // Godot expects a positive value
  2081. position.y *= -1;
  2082. return position;
  2083. }
  2084. int OS_OSX::get_screen_dpi(int p_screen) const {
  2085. if (p_screen < 0) {
  2086. p_screen = get_current_screen();
  2087. }
  2088. NSArray *screenArray = [NSScreen screens];
  2089. if ((NSUInteger)p_screen < [screenArray count]) {
  2090. NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
  2091. const NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
  2092. const CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
  2093. float scale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
  2094. float den2 = (displayPhysicalSize.width / 25.4f) * (displayPhysicalSize.width / 25.4f) + (displayPhysicalSize.height / 25.4f) * (displayPhysicalSize.height / 25.4f);
  2095. if (den2 > 0.0f) {
  2096. return ceil(sqrt(displayPixelSize.width * displayPixelSize.width + displayPixelSize.height * displayPixelSize.height) / sqrt(den2) * scale);
  2097. }
  2098. }
  2099. return 72;
  2100. }
  2101. Size2 OS_OSX::get_screen_size(int p_screen) const {
  2102. if (p_screen < 0) {
  2103. p_screen = get_current_screen();
  2104. }
  2105. NSArray *screenArray = [NSScreen screens];
  2106. if ((NSUInteger)p_screen < [screenArray count]) {
  2107. float displayScale = get_screen_max_scale();
  2108. // Note: Use frame to get the whole screen size
  2109. NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
  2110. return Size2(nsrect.size.width, nsrect.size.height) * displayScale;
  2111. }
  2112. return Size2();
  2113. }
  2114. void OS_OSX::_update_window() {
  2115. bool borderless_full = false;
  2116. if (get_borderless_window()) {
  2117. NSRect frameRect = [window_object frame];
  2118. NSRect screenRect = [[window_object screen] frame];
  2119. // Check if our window covers up the screen
  2120. if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y &&
  2121. frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) {
  2122. borderless_full = true;
  2123. }
  2124. }
  2125. if (borderless_full) {
  2126. // If the window covers up the screen set the level to above the main menu and hide on deactivate
  2127. [window_object setLevel:NSMainMenuWindowLevel + 1];
  2128. [window_object setHidesOnDeactivate:YES];
  2129. } else {
  2130. // Reset these when our window is not a borderless window that covers up the screen
  2131. if (on_top & !zoomed) {
  2132. [window_object setLevel:NSFloatingWindowLevel];
  2133. } else {
  2134. [window_object setLevel:NSNormalWindowLevel];
  2135. }
  2136. [window_object setHidesOnDeactivate:NO];
  2137. }
  2138. }
  2139. float OS_OSX::get_screen_scale(int p_screen) const {
  2140. if (p_screen < 0) {
  2141. p_screen = get_current_screen();
  2142. }
  2143. if (is_hidpi_allowed()) {
  2144. NSArray *screenArray = [NSScreen screens];
  2145. if ((NSUInteger)p_screen < [screenArray count]) {
  2146. if ([[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) {
  2147. return fmax(1.0, [[screenArray objectAtIndex:p_screen] backingScaleFactor]);
  2148. }
  2149. }
  2150. }
  2151. return 1.f;
  2152. }
  2153. float OS_OSX::get_screen_max_scale() const {
  2154. static float scale = 1.f;
  2155. if (displays_scale_dirty) {
  2156. int screen_count = get_screen_count();
  2157. for (int i = 0; i < screen_count; i++) {
  2158. scale = fmax(scale, get_screen_scale(i));
  2159. }
  2160. displays_scale_dirty = false;
  2161. }
  2162. return scale;
  2163. }
  2164. Point2 OS_OSX::get_native_window_position() const {
  2165. NSRect nsrect = [window_object frame];
  2166. Point2 pos;
  2167. float display_scale = get_screen_max_scale();
  2168. // Return the position of the top-left corner, for OS X the y starts at the bottom
  2169. pos.x = nsrect.origin.x * display_scale;
  2170. pos.y = (nsrect.origin.y + nsrect.size.height) * display_scale;
  2171. return pos;
  2172. };
  2173. Point2 OS_OSX::get_window_position() const {
  2174. Point2 position = get_native_window_position() - get_screens_origin();
  2175. // OS X native y-coordinate relative to get_screens_origin() is negative,
  2176. // Godot expects a positive value
  2177. position.y *= -1;
  2178. return position;
  2179. }
  2180. void OS_OSX::set_native_window_position(const Point2 &p_position) {
  2181. if (is_no_window_mode_enabled()) {
  2182. return;
  2183. }
  2184. NSPoint pos;
  2185. float displayScale = get_screen_max_scale();
  2186. pos.x = p_position.x / displayScale;
  2187. pos.y = p_position.y / displayScale;
  2188. [window_object setFrameTopLeftPoint:pos];
  2189. _update_window();
  2190. };
  2191. void OS_OSX::set_window_position(const Point2 &p_position) {
  2192. if (is_no_window_mode_enabled()) {
  2193. return;
  2194. }
  2195. Point2 position = p_position;
  2196. // OS X native y-coordinate relative to get_screens_origin() is negative,
  2197. // Godot passes a positive value
  2198. position.y *= -1;
  2199. set_native_window_position(get_screens_origin() + position);
  2200. update_real_mouse_position();
  2201. };
  2202. Size2 OS_OSX::get_window_size() const {
  2203. return window_size;
  2204. };
  2205. Size2 OS_OSX::get_real_window_size() const {
  2206. NSRect frame = [window_object frame];
  2207. return Size2(frame.size.width, frame.size.height) * get_screen_max_scale();
  2208. }
  2209. Size2 OS_OSX::get_max_window_size() const {
  2210. return max_size;
  2211. }
  2212. Size2 OS_OSX::get_min_window_size() const {
  2213. return min_size;
  2214. }
  2215. void OS_OSX::set_min_window_size(const Size2 p_size) {
  2216. if (is_no_window_mode_enabled()) {
  2217. return;
  2218. }
  2219. if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) {
  2220. ERR_PRINT("Minimum window size can't be larger than maximum window size!");
  2221. return;
  2222. }
  2223. min_size = p_size;
  2224. if ((min_size != Size2()) && !zoomed) {
  2225. Size2 size = min_size / get_screen_max_scale();
  2226. [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
  2227. } else {
  2228. [window_object setContentMinSize:NSMakeSize(0, 0)];
  2229. }
  2230. }
  2231. void OS_OSX::set_max_window_size(const Size2 p_size) {
  2232. if (is_no_window_mode_enabled()) {
  2233. return;
  2234. }
  2235. if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) {
  2236. ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
  2237. return;
  2238. }
  2239. max_size = p_size;
  2240. if ((max_size != Size2()) && !zoomed) {
  2241. Size2 size = max_size / get_screen_max_scale();
  2242. [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
  2243. } else {
  2244. [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
  2245. }
  2246. }
  2247. void OS_OSX::set_window_size(const Size2 p_size) {
  2248. if (is_no_window_mode_enabled()) {
  2249. return;
  2250. }
  2251. Size2 size = p_size / get_screen_max_scale();
  2252. NSPoint top_left;
  2253. NSRect old_frame = [window_object frame];
  2254. top_left.x = old_frame.origin.x;
  2255. top_left.y = NSMaxY(old_frame);
  2256. NSRect new_frame = NSMakeRect(0, 0, size.x, size.y);
  2257. new_frame = [window_object frameRectForContentRect:new_frame];
  2258. new_frame.origin.x = top_left.x;
  2259. new_frame.origin.y = top_left.y - new_frame.size.height;
  2260. [window_object setFrame:new_frame display:YES];
  2261. _update_window();
  2262. };
  2263. void OS_OSX::set_window_fullscreen(bool p_enabled) {
  2264. if (is_no_window_mode_enabled()) {
  2265. return;
  2266. }
  2267. if (zoomed != p_enabled) {
  2268. [window_object setLevel:NSNormalWindowLevel];
  2269. if (layered_window)
  2270. set_window_per_pixel_transparency_enabled(false);
  2271. if (!resizable)
  2272. [window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable];
  2273. if (p_enabled) {
  2274. [window_object setContentMinSize:NSMakeSize(0, 0)];
  2275. [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
  2276. } else {
  2277. if (min_size != Size2()) {
  2278. Size2 size = min_size / get_screen_max_scale();
  2279. [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
  2280. }
  2281. if (max_size != Size2()) {
  2282. Size2 size = max_size / get_screen_max_scale();
  2283. [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
  2284. }
  2285. }
  2286. [window_object toggleFullScreen:nil];
  2287. }
  2288. zoomed = p_enabled;
  2289. };
  2290. bool OS_OSX::is_window_fullscreen() const {
  2291. return zoomed;
  2292. };
  2293. void OS_OSX::set_window_resizable(bool p_enabled) {
  2294. if (is_no_window_mode_enabled()) {
  2295. return;
  2296. }
  2297. if (p_enabled)
  2298. [window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable];
  2299. else if (!zoomed)
  2300. [window_object setStyleMask:[window_object styleMask] & ~NSWindowStyleMaskResizable];
  2301. resizable = p_enabled;
  2302. };
  2303. bool OS_OSX::is_window_resizable() const {
  2304. return [window_object styleMask] & NSWindowStyleMaskResizable;
  2305. };
  2306. void OS_OSX::set_window_minimized(bool p_enabled) {
  2307. if (is_no_window_mode_enabled()) {
  2308. return;
  2309. }
  2310. if (p_enabled)
  2311. [window_object performMiniaturize:nil];
  2312. else
  2313. [window_object deminiaturize:nil];
  2314. };
  2315. bool OS_OSX::is_window_minimized() const {
  2316. if ([window_object respondsToSelector:@selector(isMiniaturized)])
  2317. return [window_object isMiniaturized];
  2318. return minimized;
  2319. };
  2320. void OS_OSX::set_window_maximized(bool p_enabled) {
  2321. if (is_no_window_mode_enabled()) {
  2322. return;
  2323. }
  2324. if (p_enabled) {
  2325. restore_rect = Rect2(get_window_position(), get_window_size());
  2326. [window_object setFrame:[[[NSScreen screens] objectAtIndex:get_current_screen()] visibleFrame] display:YES];
  2327. } else {
  2328. set_window_size(restore_rect.size);
  2329. set_window_position(restore_rect.position);
  2330. };
  2331. maximized = p_enabled;
  2332. };
  2333. bool OS_OSX::is_window_maximized() const {
  2334. // don't know
  2335. return maximized;
  2336. };
  2337. void OS_OSX::move_window_to_foreground() {
  2338. if (is_no_window_mode_enabled()) {
  2339. return;
  2340. }
  2341. [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
  2342. [window_object makeKeyAndOrderFront:nil];
  2343. }
  2344. void OS_OSX::set_window_always_on_top(bool p_enabled) {
  2345. if (is_no_window_mode_enabled()) {
  2346. return;
  2347. }
  2348. on_top = p_enabled;
  2349. if (zoomed)
  2350. return;
  2351. if (is_window_always_on_top() == p_enabled)
  2352. return;
  2353. if (p_enabled)
  2354. [window_object setLevel:NSFloatingWindowLevel];
  2355. else
  2356. [window_object setLevel:NSNormalWindowLevel];
  2357. }
  2358. bool OS_OSX::is_window_always_on_top() const {
  2359. if (zoomed) {
  2360. return on_top;
  2361. } else {
  2362. return [window_object level] == NSFloatingWindowLevel;
  2363. }
  2364. }
  2365. bool OS_OSX::is_window_focused() const {
  2366. return window_focused;
  2367. }
  2368. void OS_OSX::request_attention() {
  2369. if (is_no_window_mode_enabled()) {
  2370. return;
  2371. }
  2372. [NSApp requestUserAttention:NSCriticalRequest];
  2373. }
  2374. bool OS_OSX::get_window_per_pixel_transparency_enabled() const {
  2375. if (!is_layered_allowed())
  2376. return false;
  2377. return layered_window;
  2378. }
  2379. void OS_OSX::set_window_per_pixel_transparency_enabled(bool p_enabled) {
  2380. if (!is_layered_allowed())
  2381. return;
  2382. if (layered_window != p_enabled) {
  2383. if (p_enabled) {
  2384. GLint opacity = 0;
  2385. [window_object setBackgroundColor:[NSColor clearColor]];
  2386. [window_object setOpaque:NO];
  2387. [window_object setHasShadow:NO];
  2388. [context setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
  2389. layered_window = true;
  2390. } else {
  2391. GLint opacity = 1;
  2392. [window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
  2393. [window_object setOpaque:YES];
  2394. [window_object setHasShadow:YES];
  2395. [context setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
  2396. layered_window = false;
  2397. }
  2398. [context update];
  2399. NSRect frame = [window_object frame];
  2400. if (!is_no_window_mode_enabled()) {
  2401. [window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, 1, 1) display:YES];
  2402. [window_object setFrame:frame display:YES];
  2403. }
  2404. }
  2405. }
  2406. void OS_OSX::set_borderless_window(bool p_borderless) {
  2407. if (is_no_window_mode_enabled()) {
  2408. return;
  2409. }
  2410. // OrderOut prevents a lose focus bug with the window
  2411. [window_object orderOut:nil];
  2412. if (p_borderless) {
  2413. [window_object setStyleMask:NSWindowStyleMaskBorderless];
  2414. } else {
  2415. [window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (resizable ? NSWindowStyleMaskResizable : 0)];
  2416. // Force update of the window styles
  2417. NSRect frameRect = [window_object frame];
  2418. [window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
  2419. [window_object setFrame:frameRect display:NO];
  2420. // Restore the window title
  2421. [window_object setTitle:[NSString stringWithUTF8String:title.utf8().get_data()]];
  2422. }
  2423. _update_window();
  2424. [window_object makeKeyAndOrderFront:nil];
  2425. }
  2426. bool OS_OSX::get_borderless_window() {
  2427. return [window_object styleMask] == NSWindowStyleMaskBorderless;
  2428. }
  2429. String OS_OSX::get_executable_path() const {
  2430. int ret;
  2431. pid_t pid;
  2432. char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
  2433. pid = getpid();
  2434. ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
  2435. if (ret <= 0) {
  2436. return OS::get_executable_path();
  2437. } else {
  2438. String path;
  2439. path.parse_utf8(pathbuf);
  2440. return path;
  2441. }
  2442. }
  2443. // Returns string representation of keys, if they are printable.
  2444. //
  2445. static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) {
  2446. TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
  2447. if (!currentKeyboard)
  2448. return nil;
  2449. CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
  2450. if (!layoutData)
  2451. return nil;
  2452. const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
  2453. OSStatus err;
  2454. CFMutableStringRef output = CFStringCreateMutable(NULL, 0);
  2455. for (int i = 0; i < length; ++i) {
  2456. UInt32 keysDown = 0;
  2457. UniChar chars[4];
  2458. UniCharCount realLength;
  2459. err = UCKeyTranslate(keyboardLayout,
  2460. keyCode[i],
  2461. kUCKeyActionDisplay,
  2462. 0,
  2463. LMGetKbdType(),
  2464. kUCKeyTranslateNoDeadKeysBit,
  2465. &keysDown,
  2466. sizeof(chars) / sizeof(chars[0]),
  2467. &realLength,
  2468. chars);
  2469. if (err != noErr) {
  2470. CFRelease(output);
  2471. return nil;
  2472. }
  2473. CFStringAppendCharacters(output, chars, 1);
  2474. }
  2475. //CFStringUppercase(output, NULL);
  2476. return (NSString *)output;
  2477. }
  2478. void _update_keyboard_layouts() {
  2479. @autoreleasepool {
  2480. TISInputSourceRef cur_source = TISCopyCurrentKeyboardInputSource();
  2481. NSString *cur_name = (NSString *)TISGetInputSourceProperty(cur_source, kTISPropertyLocalizedName);
  2482. CFRelease(cur_source);
  2483. // Enum IME layouts
  2484. NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode };
  2485. NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false);
  2486. for (NSUInteger i = 0; i < [list_ime count]; i++) {
  2487. LayoutInfo ly;
  2488. NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
  2489. ly.name.parse_utf8([name UTF8String]);
  2490. NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages);
  2491. ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
  2492. kbd_layouts.push_back(ly);
  2493. if ([name isEqualToString:cur_name]) {
  2494. current_layout = kbd_layouts.size() - 1;
  2495. }
  2496. }
  2497. [list_ime release];
  2498. // Enum plain keyboard layouts
  2499. NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout };
  2500. NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false);
  2501. for (NSUInteger i = 0; i < [list_kbd count]; i++) {
  2502. LayoutInfo ly;
  2503. NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
  2504. ly.name.parse_utf8([name UTF8String]);
  2505. NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyInputSourceLanguages);
  2506. ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
  2507. kbd_layouts.push_back(ly);
  2508. if ([name isEqualToString:cur_name]) {
  2509. current_layout = kbd_layouts.size() - 1;
  2510. }
  2511. }
  2512. [list_kbd release];
  2513. }
  2514. // Update latin variant
  2515. latin_variant = OS::LATIN_KEYBOARD_QWERTY;
  2516. CGKeyCode keys[] = { kVK_ANSI_Q, kVK_ANSI_W, kVK_ANSI_E, kVK_ANSI_R, kVK_ANSI_T, kVK_ANSI_Y };
  2517. NSString *test = createStringForKeys(keys, 6);
  2518. if ([test isEqualToString:@"qwertz"]) {
  2519. latin_variant = OS::LATIN_KEYBOARD_QWERTZ;
  2520. } else if ([test isEqualToString:@"azerty"]) {
  2521. latin_variant = OS::LATIN_KEYBOARD_AZERTY;
  2522. } else if ([test isEqualToString:@"qzerty"]) {
  2523. latin_variant = OS::LATIN_KEYBOARD_QZERTY;
  2524. } else if ([test isEqualToString:@"',.pyf"]) {
  2525. latin_variant = OS::LATIN_KEYBOARD_DVORAK;
  2526. } else if ([test isEqualToString:@"xvlcwk"]) {
  2527. latin_variant = OS::LATIN_KEYBOARD_NEO;
  2528. } else if ([test isEqualToString:@"qwfpgj"]) {
  2529. latin_variant = OS::LATIN_KEYBOARD_COLEMAK;
  2530. }
  2531. [test release];
  2532. keyboard_layout_dirty = false;
  2533. }
  2534. OS::LatinKeyboardVariant OS_OSX::get_latin_keyboard_variant() const {
  2535. if (keyboard_layout_dirty) {
  2536. _update_keyboard_layouts();
  2537. }
  2538. return latin_variant;
  2539. }
  2540. int OS_OSX::keyboard_get_layout_count() const {
  2541. if (keyboard_layout_dirty) {
  2542. _update_keyboard_layouts();
  2543. }
  2544. return kbd_layouts.size();
  2545. }
  2546. void OS_OSX::keyboard_set_current_layout(int p_index) {
  2547. if (keyboard_layout_dirty) {
  2548. _update_keyboard_layouts();
  2549. }
  2550. ERR_FAIL_INDEX(p_index, kbd_layouts.size());
  2551. NSString *cur_name = [NSString stringWithUTF8String:kbd_layouts[p_index].name.utf8().get_data()];
  2552. NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout };
  2553. NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false);
  2554. for (NSUInteger i = 0; i < [list_kbd count]; i++) {
  2555. NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
  2556. if ([name isEqualToString:cur_name]) {
  2557. TISSelectInputSource((TISInputSourceRef)[list_kbd objectAtIndex:i]);
  2558. break;
  2559. }
  2560. }
  2561. [list_kbd release];
  2562. NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode };
  2563. NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false);
  2564. for (NSUInteger i = 0; i < [list_ime count]; i++) {
  2565. NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
  2566. if ([name isEqualToString:cur_name]) {
  2567. TISSelectInputSource((TISInputSourceRef)[list_ime objectAtIndex:i]);
  2568. break;
  2569. }
  2570. }
  2571. [list_ime release];
  2572. }
  2573. int OS_OSX::keyboard_get_current_layout() const {
  2574. if (keyboard_layout_dirty) {
  2575. _update_keyboard_layouts();
  2576. }
  2577. return current_layout;
  2578. }
  2579. String OS_OSX::keyboard_get_layout_language(int p_index) const {
  2580. if (keyboard_layout_dirty) {
  2581. _update_keyboard_layouts();
  2582. }
  2583. ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
  2584. return kbd_layouts[p_index].code;
  2585. }
  2586. String OS_OSX::keyboard_get_layout_name(int p_index) const {
  2587. if (keyboard_layout_dirty) {
  2588. _update_keyboard_layouts();
  2589. }
  2590. ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
  2591. return kbd_layouts[p_index].name;
  2592. }
  2593. void OS_OSX::process_events() {
  2594. while (true) {
  2595. NSEvent *event = [NSApp
  2596. nextEventMatchingMask:NSEventMaskAny
  2597. untilDate:[NSDate distantPast]
  2598. inMode:NSDefaultRunLoopMode
  2599. dequeue:YES];
  2600. if (event == nil)
  2601. break;
  2602. [NSApp sendEvent:event];
  2603. }
  2604. process_key_events();
  2605. if (mpath.size() > 0) {
  2606. const Vector2 mpos = get_mouse_pos([window_object mouseLocationOutsideOfEventStream]);
  2607. if (Geometry::is_point_in_polygon(mpos, mpath)) {
  2608. if ([window_object ignoresMouseEvents]) {
  2609. [window_object setIgnoresMouseEvents:NO];
  2610. }
  2611. } else {
  2612. if (![window_object ignoresMouseEvents]) {
  2613. [window_object setIgnoresMouseEvents:YES];
  2614. }
  2615. }
  2616. } else {
  2617. if ([window_object ignoresMouseEvents]) {
  2618. [window_object setIgnoresMouseEvents:NO];
  2619. }
  2620. }
  2621. [autoreleasePool drain];
  2622. autoreleasePool = [[NSAutoreleasePool alloc] init];
  2623. input->flush_accumulated_events();
  2624. }
  2625. void OS_OSX::process_key_events() {
  2626. Ref<InputEventKey> k;
  2627. for (int i = 0; i < key_event_pos; i++) {
  2628. const KeyEvent &ke = key_event_buffer[i];
  2629. if (ke.raw) {
  2630. // Non IME input - no composite characters, pass events as is
  2631. k.instance();
  2632. get_key_modifier_state(ke.osx_state, k);
  2633. k->set_pressed(ke.pressed);
  2634. k->set_echo(ke.echo);
  2635. k->set_scancode(ke.scancode);
  2636. k->set_physical_scancode(ke.physical_scancode);
  2637. k->set_unicode(ke.unicode);
  2638. push_input(k);
  2639. } else {
  2640. // IME input
  2641. if ((i == 0 && ke.scancode == 0) || (i > 0 && key_event_buffer[i - 1].scancode == 0)) {
  2642. k.instance();
  2643. get_key_modifier_state(ke.osx_state, k);
  2644. k->set_pressed(ke.pressed);
  2645. k->set_echo(ke.echo);
  2646. k->set_scancode(0);
  2647. k->set_physical_scancode(0);
  2648. k->set_unicode(ke.unicode);
  2649. push_input(k);
  2650. }
  2651. if (ke.scancode != 0) {
  2652. k.instance();
  2653. get_key_modifier_state(ke.osx_state, k);
  2654. k->set_pressed(ke.pressed);
  2655. k->set_echo(ke.echo);
  2656. k->set_scancode(ke.scancode);
  2657. k->set_physical_scancode(ke.physical_scancode);
  2658. if (i + 1 < key_event_pos && key_event_buffer[i + 1].scancode == 0) {
  2659. k->set_unicode(key_event_buffer[i + 1].unicode);
  2660. }
  2661. push_input(k);
  2662. }
  2663. }
  2664. }
  2665. key_event_pos = 0;
  2666. }
  2667. void OS_OSX::push_input(const Ref<InputEvent> &p_event) {
  2668. Ref<InputEvent> ev = p_event;
  2669. input->accumulate_input_event(ev);
  2670. }
  2671. void OS_OSX::force_process_input() {
  2672. process_events(); // get rid of pending events
  2673. joypad_osx->process_joypads();
  2674. }
  2675. void OS_OSX::run() {
  2676. force_quit = false;
  2677. if (!main_loop)
  2678. return;
  2679. main_loop->init();
  2680. if (zoomed) {
  2681. zoomed = false;
  2682. set_window_fullscreen(true);
  2683. }
  2684. //uint64_t last_ticks=get_ticks_usec();
  2685. //int frames=0;
  2686. //uint64_t frame=0;
  2687. bool quit = false;
  2688. while (!force_quit && !quit) {
  2689. @try {
  2690. process_events(); // get rid of pending events
  2691. joypad_osx->process_joypads();
  2692. if (Main::iteration()) {
  2693. quit = true;
  2694. }
  2695. } @catch (NSException *exception) {
  2696. ERR_PRINTS("NSException: " + String([exception reason].UTF8String));
  2697. }
  2698. };
  2699. main_loop->finish();
  2700. }
  2701. void OS_OSX::set_mouse_mode(MouseMode p_mode) {
  2702. if (p_mode == mouse_mode)
  2703. return;
  2704. if (p_mode == MOUSE_MODE_CAPTURED) {
  2705. // Apple Docs state that the display parameter is not used.
  2706. // "This parameter is not used. By default, you may pass kCGDirectMainDisplay."
  2707. // https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html
  2708. if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
  2709. CGDisplayHideCursor(kCGDirectMainDisplay);
  2710. }
  2711. CGAssociateMouseAndMouseCursorPosition(false);
  2712. const NSRect contentRect = [window_view frame];
  2713. NSRect pointInWindowRect = NSMakeRect(contentRect.size.width / 2, contentRect.size.height / 2, 0, 0);
  2714. NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin;
  2715. CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
  2716. CGWarpMouseCursorPosition(lMouseWarpPos);
  2717. } else if (p_mode == MOUSE_MODE_HIDDEN) {
  2718. if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
  2719. CGDisplayHideCursor(kCGDirectMainDisplay);
  2720. }
  2721. CGAssociateMouseAndMouseCursorPosition(true);
  2722. } else if (p_mode == MOUSE_MODE_CONFINED) {
  2723. CGDisplayShowCursor(kCGDirectMainDisplay);
  2724. CGAssociateMouseAndMouseCursorPosition(false);
  2725. } else {
  2726. CGDisplayShowCursor(kCGDirectMainDisplay);
  2727. CGAssociateMouseAndMouseCursorPosition(true);
  2728. }
  2729. last_warp = [[NSProcessInfo processInfo] systemUptime];
  2730. ignore_warp = true;
  2731. warp_events.clear();
  2732. mouse_mode = p_mode;
  2733. }
  2734. OS::MouseMode OS_OSX::get_mouse_mode() const {
  2735. return mouse_mode;
  2736. }
  2737. String OS_OSX::get_joy_guid(int p_device) const {
  2738. return input->get_joy_guid_remapped(p_device);
  2739. }
  2740. OS::PowerState OS_OSX::get_power_state() {
  2741. return power_manager->get_power_state();
  2742. }
  2743. int OS_OSX::get_power_seconds_left() {
  2744. return power_manager->get_power_seconds_left();
  2745. }
  2746. int OS_OSX::get_power_percent_left() {
  2747. return power_manager->get_power_percent_left();
  2748. }
  2749. Error OS_OSX::move_to_trash(const String &p_path) {
  2750. NSFileManager *fm = [NSFileManager defaultManager];
  2751. NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
  2752. NSError *err;
  2753. if (![fm trashItemAtURL:url resultingItemURL:nil error:&err]) {
  2754. ERR_PRINTS("trashItemAtURL error: " + String(err.localizedDescription.UTF8String));
  2755. return FAILED;
  2756. }
  2757. return OK;
  2758. }
  2759. void OS_OSX::_set_use_vsync(bool p_enable) {
  2760. CGLContextObj ctx = CGLGetCurrentContext();
  2761. if (ctx) {
  2762. GLint swapInterval = p_enable ? 1 : 0;
  2763. CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval);
  2764. }
  2765. }
  2766. OS_OSX *OS_OSX::singleton = NULL;
  2767. OS_OSX::OS_OSX() {
  2768. context = nullptr;
  2769. memset(cursors, 0, sizeof(cursors));
  2770. key_event_pos = 0;
  2771. mouse_mode = OS::MOUSE_MODE_VISIBLE;
  2772. main_loop = NULL;
  2773. singleton = this;
  2774. im_active = false;
  2775. im_position = Point2();
  2776. layered_window = false;
  2777. autoreleasePool = [[NSAutoreleasePool alloc] init];
  2778. eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
  2779. ERR_FAIL_COND(!eventSource);
  2780. CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0);
  2781. /*
  2782. if (pthread_key_create(&_Godot.nsgl.current, NULL) != 0) {
  2783. _GodotInputError(Godot_PLATFORM_ERROR, "NSGL: Failed to create context TLS");
  2784. return GL_FALSE;
  2785. }
  2786. */
  2787. framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
  2788. ERR_FAIL_COND(!framework);
  2789. // Implicitly create shared NSApplication instance
  2790. [GodotApplication sharedApplication];
  2791. // Menu bar setup must go between sharedApplication above and
  2792. // finishLaunching below, in order to properly emulate the behavior
  2793. // of NSApplicationMain
  2794. NSMenuItem *menu_item;
  2795. NSString *title;
  2796. NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
  2797. if (nsappname == nil)
  2798. nsappname = [[NSProcessInfo processInfo] processName];
  2799. // Setup Apple menu
  2800. NSMenu *apple_menu = [[NSMenu alloc] initWithTitle:@""];
  2801. title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname];
  2802. [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""];
  2803. [apple_menu addItem:[NSMenuItem separatorItem]];
  2804. NSMenu *services = [[NSMenu alloc] initWithTitle:@""];
  2805. menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""];
  2806. [apple_menu setSubmenu:services forItem:menu_item];
  2807. [NSApp setServicesMenu:services];
  2808. [services release];
  2809. [apple_menu addItem:[NSMenuItem separatorItem]];
  2810. title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname];
  2811. [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
  2812. menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
  2813. [menu_item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)];
  2814. [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""];
  2815. [apple_menu addItem:[NSMenuItem separatorItem]];
  2816. title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
  2817. [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
  2818. // Setup menu bar
  2819. NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""];
  2820. menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
  2821. [main_menu setSubmenu:apple_menu forItem:menu_item];
  2822. [NSApp setMainMenu:main_menu];
  2823. [main_menu release];
  2824. [apple_menu release];
  2825. [NSApp finishLaunching];
  2826. delegate = [[GodotApplicationDelegate alloc] init];
  2827. ERR_FAIL_COND(!delegate);
  2828. [NSApp setDelegate:delegate];
  2829. cursor_shape = CURSOR_ARROW;
  2830. maximized = false;
  2831. minimized = false;
  2832. window_size = Vector2(1024, 600);
  2833. zoomed = false;
  2834. resizable = false;
  2835. window_focused = true;
  2836. on_top = false;
  2837. Vector<Logger *> loggers;
  2838. loggers.push_back(memnew(OSXTerminalLogger));
  2839. _set_logger(memnew(CompositeLogger(loggers)));
  2840. //process application:openFile: event
  2841. while (true) {
  2842. NSEvent *event = [NSApp
  2843. nextEventMatchingMask:NSEventMaskAny
  2844. untilDate:[NSDate distantPast]
  2845. inMode:NSDefaultRunLoopMode
  2846. dequeue:YES];
  2847. if (event == nil)
  2848. break;
  2849. [NSApp sendEvent:event];
  2850. }
  2851. #ifdef COREAUDIO_ENABLED
  2852. AudioDriverManager::add_driver(&audio_driver);
  2853. #endif
  2854. }
  2855. bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
  2856. return p_feature == "pc";
  2857. }
  2858. void OS_OSX::disable_crash_handler() {
  2859. crash_handler.disable();
  2860. }
  2861. bool OS_OSX::is_disable_crash_handler() const {
  2862. return crash_handler.is_disabled();
  2863. }