window.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. #if defined(Hiro_Window)
  2. @implementation CocoaWindow : NSWindow
  3. -(id) initWith:(hiro::mWindow&)windowReference {
  4. window = &windowReference;
  5. NSUInteger style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
  6. if(window->state.resizable) style |= NSResizableWindowMask;
  7. if(self = [super initWithContentRect:NSMakeRect(0, 0, 640, 480) styleMask:style backing:NSBackingStoreBuffered defer:YES]) {
  8. [self setDelegate:self];
  9. [self setReleasedWhenClosed:NO];
  10. [self setAcceptsMouseMovedEvents:YES];
  11. [self setTitle:@""];
  12. NSBundle* bundle = [NSBundle mainBundle];
  13. NSDictionary* dictionary = [bundle infoDictionary];
  14. NSString* applicationName = [dictionary objectForKey:@"CFBundleDisplayName"];
  15. string hiroName = hiro::Application::state().name ? hiro::Application::state().name : string{"hiro"};
  16. if(applicationName == nil) applicationName = [NSString stringWithUTF8String:hiroName];
  17. menuBar = [[NSMenu alloc] init];
  18. NSMenuItem* item;
  19. string text;
  20. rootMenu = [[NSMenu alloc] init];
  21. item = [[[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""] autorelease];
  22. [item setSubmenu:rootMenu];
  23. [menuBar addItem:item];
  24. item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"About %@ ...", applicationName] action:@selector(menuAbout) keyEquivalent:@""] autorelease];
  25. [item setTarget:self];
  26. [rootMenu addItem:item];
  27. [rootMenu addItem:[NSMenuItem separatorItem]];
  28. item = [[[NSMenuItem alloc] initWithTitle:@"Preferences ..." action:@selector(menuPreferences) keyEquivalent:@""] autorelease];
  29. [item setTarget:self];
  30. [rootMenu addItem:item];
  31. string result = nall::execute("spctl", "--status").output.strip();
  32. if(result != "assessments disabled") {
  33. disableGatekeeper = [[[NSMenuItem alloc] initWithTitle:@"Disable Gatekeeper" action:@selector(menuDisableGatekeeper) keyEquivalent:@""] autorelease];
  34. [disableGatekeeper setTarget:self];
  35. [rootMenu addItem:disableGatekeeper];
  36. }
  37. [rootMenu addItem:[NSMenuItem separatorItem]];
  38. NSMenu* servicesMenu = [[[NSMenu alloc] initWithTitle:@"Services"] autorelease];
  39. item = [[[NSMenuItem alloc] initWithTitle:@"Services" action:nil keyEquivalent:@""] autorelease];
  40. [item setTarget:self];
  41. [item setSubmenu:servicesMenu];
  42. [rootMenu addItem:item];
  43. [rootMenu addItem:[NSMenuItem separatorItem]];
  44. [NSApp setServicesMenu:servicesMenu];
  45. item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"Hide %@", applicationName] action:@selector(hide:) keyEquivalent:@""] autorelease];
  46. [item setTarget:NSApp];
  47. [rootMenu addItem:item];
  48. item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@""] autorelease];
  49. [item setTarget:NSApp];
  50. [rootMenu addItem:item];
  51. item = [[[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""] autorelease];
  52. [item setTarget:NSApp];
  53. [rootMenu addItem:item];
  54. [rootMenu addItem:[NSMenuItem separatorItem]];
  55. item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"Quit %@", applicationName] action:@selector(menuQuit) keyEquivalent:@""] autorelease];
  56. [item setTarget:self];
  57. [rootMenu addItem:item];
  58. statusBar = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)];
  59. [statusBar setAlignment:NSLeftTextAlignment];
  60. [statusBar setBordered:YES];
  61. [statusBar setBezeled:YES];
  62. [statusBar setBezelStyle:NSTextFieldSquareBezel];
  63. [statusBar setEditable:NO];
  64. [statusBar setHidden:YES];
  65. [[self contentView] addSubview:statusBar positioned:NSWindowBelow relativeTo:nil];
  66. }
  67. return self;
  68. }
  69. -(BOOL) canBecomeKeyWindow {
  70. return YES;
  71. }
  72. -(BOOL) canBecomeMainWindow {
  73. return YES;
  74. }
  75. -(void) windowDidBecomeMain:(NSNotification*)notification {
  76. if(window->state.menuBar) {
  77. [NSApp setMainMenu:menuBar];
  78. }
  79. }
  80. -(void) windowDidMove:(NSNotification*)notification {
  81. if(auto p = window->self()) p->moveEvent();
  82. }
  83. -(void) windowDidResize:(NSNotification*)notification {
  84. if(auto p = window->self()) p->sizeEvent();
  85. }
  86. -(BOOL) windowShouldClose:(id)sender {
  87. if(window->state.onClose) window->doClose();
  88. else window->setVisible(false);
  89. if(window->state.modal && !window->visible()) window->setModal(false);
  90. return NO;
  91. }
  92. -(NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender {
  93. return DropPathsOperation(sender);
  94. }
  95. -(BOOL) performDragOperation:(id<NSDraggingInfo>)sender {
  96. auto paths = DropPaths(sender);
  97. if(!paths) return NO;
  98. window->doDrop(paths);
  99. return YES;
  100. }
  101. -(NSMenu*) menuBar {
  102. return menuBar;
  103. }
  104. -(void) menuAbout {
  105. hiro::Application::Cocoa::doAbout();
  106. }
  107. -(void) menuPreferences {
  108. hiro::Application::Cocoa::doPreferences();
  109. }
  110. //to hell with gatekeepers
  111. -(void) menuDisableGatekeeper {
  112. NSAlert* alert = [[[NSAlert alloc] init] autorelease];
  113. [alert setMessageText:@"Disable Gatekeeper"];
  114. AuthorizationRef authorization;
  115. OSStatus status = AuthorizationCreate(nullptr, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorization);
  116. if(status == errAuthorizationSuccess) {
  117. AuthorizationItem items = {kAuthorizationRightExecute, 0, nullptr, 0};
  118. AuthorizationRights rights = {1, &items};
  119. status = AuthorizationCopyRights(authorization, &rights, nullptr,
  120. kAuthorizationFlagDefaults
  121. | kAuthorizationFlagInteractionAllowed
  122. | kAuthorizationFlagPreAuthorize
  123. | kAuthorizationFlagExtendRights, nullptr);
  124. if(status == errAuthorizationSuccess) {
  125. { char program[] = "/usr/sbin/spctl";
  126. char* arguments[] = {"--master-disable", nullptr};
  127. FILE* pipe = nullptr;
  128. AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
  129. }
  130. { char program[] = "/usr/bin/defaults";
  131. char* arguments[] = {"write /Library/Preferences/com.apple.security GKAutoRearm -bool NO"};
  132. FILE* pipe = nullptr;
  133. AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
  134. }
  135. }
  136. AuthorizationFree(authorization, kAuthorizationFlagDefaults);
  137. }
  138. string result = nall::execute("spctl", "--status").output.strip();
  139. if(result == "assessments disabled") {
  140. [alert setAlertStyle:NSInformationalAlertStyle];
  141. [alert setInformativeText:@"Gatekeeper has been successfully disabled."];
  142. [disableGatekeeper setHidden:YES];
  143. } else {
  144. [alert setAlertStyle:NSWarningAlertStyle];
  145. [alert setInformativeText:@"Error: failed to disable Gatekeeper."];
  146. }
  147. [alert addButtonWithTitle:@"Ok"];
  148. [alert runModal];
  149. }
  150. -(void) menuQuit {
  151. hiro::Application::Cocoa::doQuit();
  152. }
  153. -(NSTextField*) statusBar {
  154. return statusBar;
  155. }
  156. @end
  157. namespace hiro {
  158. auto pWindow::construct() -> void {
  159. @autoreleasepool {
  160. cocoaWindow = [[CocoaWindow alloc] initWith:self()];
  161. static bool once = true;
  162. if(once) {
  163. once = false;
  164. [NSApp setMainMenu:[cocoaWindow menuBar]];
  165. }
  166. }
  167. }
  168. auto pWindow::destruct() -> void {
  169. @autoreleasepool {
  170. [cocoaWindow release];
  171. }
  172. }
  173. auto pWindow::append(sMenuBar menuBar) -> void {
  174. }
  175. auto pWindow::append(sSizable sizable) -> void {
  176. sizable->setGeometry(self().geometry().setPosition());
  177. statusBarReposition();
  178. }
  179. auto pWindow::append(sStatusBar statusBar) -> void {
  180. statusBar->setEnabled(statusBar->enabled(true));
  181. statusBar->setFont(statusBar->font(true));
  182. statusBar->setText(statusBar->text());
  183. statusBar->setVisible(statusBar->visible(true));
  184. }
  185. auto pWindow::focused() const -> bool {
  186. @autoreleasepool {
  187. return [cocoaWindow isMainWindow] == YES;
  188. }
  189. }
  190. auto pWindow::frameMargin() const -> Geometry {
  191. @autoreleasepool {
  192. NSRect frame = [cocoaWindow frameRectForContentRect:NSMakeRect(0, 0, 640, 480)];
  193. return {abs(frame.origin.x), (int)(frame.size.height - 480), (int)(frame.size.width - 640), abs(frame.origin.y)};
  194. }
  195. }
  196. auto pWindow::handle() const -> uintptr_t {
  197. return (uintptr_t)cocoaWindow;
  198. }
  199. auto pWindow::monitor() const -> uint {
  200. //TODO
  201. return 0;
  202. }
  203. auto pWindow::remove(sMenuBar menuBar) -> void {
  204. }
  205. auto pWindow::remove(sSizable sizable) -> void {
  206. @autoreleasepool {
  207. [[cocoaWindow contentView] setNeedsDisplay:YES];
  208. }
  209. }
  210. auto pWindow::remove(sStatusBar statusBar) -> void {
  211. @autoreleasepool {
  212. [[cocoaWindow statusBar] setHidden:YES];
  213. }
  214. }
  215. auto pWindow::setBackgroundColor(Color color) -> void {
  216. @autoreleasepool {
  217. [cocoaWindow
  218. setBackgroundColor:[NSColor
  219. colorWithCalibratedRed:color.red() / 255.0
  220. green:color.green() / 255.0
  221. blue:color.blue() / 255.0
  222. alpha:color.alpha() / 255.0
  223. ]
  224. ];
  225. }
  226. }
  227. auto pWindow::setDismissable(bool dismissable) -> void {
  228. //todo: not implemented
  229. }
  230. auto pWindow::setDroppable(bool droppable) -> void {
  231. @autoreleasepool {
  232. if(droppable) {
  233. [cocoaWindow registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
  234. } else {
  235. [cocoaWindow unregisterDraggedTypes];
  236. }
  237. }
  238. }
  239. auto pWindow::setFocused() -> void {
  240. @autoreleasepool {
  241. [cocoaWindow makeKeyAndOrderFront:nil];
  242. }
  243. }
  244. auto pWindow::setFullScreen(bool fullScreen) -> void {
  245. @autoreleasepool {
  246. if(fullScreen) {
  247. windowedGeometry = state().geometry;
  248. [NSApp setPresentationOptions:NSApplicationPresentationFullScreen];
  249. [cocoaWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  250. [cocoaWindow toggleFullScreen:nil];
  251. state().geometry = _geometry();
  252. } else {
  253. [cocoaWindow toggleFullScreen:nil];
  254. [cocoaWindow setCollectionBehavior:NSWindowCollectionBehaviorDefault];
  255. [NSApp setPresentationOptions:NSApplicationPresentationDefault];
  256. state().geometry = windowedGeometry;
  257. }
  258. }
  259. }
  260. auto pWindow::setGeometry(Geometry geometry) -> void {
  261. lock();
  262. @autoreleasepool {
  263. [cocoaWindow
  264. setFrame:[cocoaWindow
  265. frameRectForContentRect:NSMakeRect(
  266. geometry.x(), Desktop::size().height() - geometry.y() - geometry.height(),
  267. geometry.width(), geometry.height() + statusBarHeight()
  268. )
  269. ]
  270. display:YES
  271. ];
  272. if(auto& sizable = state().sizable) {
  273. sizable->setGeometry(self().geometry().setPosition());
  274. }
  275. statusBarReposition();
  276. }
  277. unlock();
  278. }
  279. auto pWindow::setMaximized(bool maximized) -> void {
  280. //todo
  281. }
  282. auto pWindow::setMaximumSize(Size size) -> void {
  283. //todo
  284. }
  285. auto pWindow::setMinimized(bool minimized) -> void {
  286. //todo
  287. }
  288. auto pWindow::setMinimumSize(Size size) -> void {
  289. //todo
  290. }
  291. auto pWindow::setModal(bool modal) -> void {
  292. @autoreleasepool {
  293. if(modal == true) {
  294. [NSApp runModalForWindow:cocoaWindow];
  295. } else {
  296. [NSApp stopModal];
  297. NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0];
  298. [NSApp postEvent:event atStart:true];
  299. }
  300. }
  301. }
  302. auto pWindow::setResizable(bool resizable) -> void {
  303. @autoreleasepool {
  304. NSUInteger style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
  305. if(resizable) style |= NSResizableWindowMask;
  306. [cocoaWindow setStyleMask:style];
  307. }
  308. }
  309. auto pWindow::setTitle(const string& text) -> void {
  310. @autoreleasepool {
  311. [cocoaWindow setTitle:[NSString stringWithUTF8String:text]];
  312. }
  313. }
  314. auto pWindow::setVisible(bool visible) -> void {
  315. @autoreleasepool {
  316. if(visible) [cocoaWindow makeKeyAndOrderFront:nil];
  317. else [cocoaWindow orderOut:nil];
  318. }
  319. }
  320. auto pWindow::moveEvent() -> void {
  321. if(!locked() && !self().fullScreen() && self().visible()) {
  322. @autoreleasepool {
  323. NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]];
  324. area.size.height -= statusBarHeight();
  325. state().geometry.setX(area.origin.x);
  326. state().geometry.setY(Desktop::size().height() - area.origin.y - area.size.height);
  327. }
  328. }
  329. if(!locked()) self().doMove();
  330. }
  331. auto pWindow::sizeEvent() -> void {
  332. if(!locked() && !self().fullScreen() && self().visible()) {
  333. @autoreleasepool {
  334. NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]];
  335. area.size.height -= statusBarHeight();
  336. state().geometry.setWidth(area.size.width);
  337. state().geometry.setHeight(area.size.height);
  338. }
  339. }
  340. if(auto& sizable = state().sizable) {
  341. sizable->setGeometry(self().geometry().setPosition());
  342. }
  343. statusBarReposition();
  344. if(!locked()) self().doSize();
  345. }
  346. auto pWindow::statusBarHeight() -> uint {
  347. if(auto& statusBar = state().statusBar) {
  348. if(statusBar->visible()) {
  349. return pFont::size(statusBar->font(true), " ").height() + 6;
  350. }
  351. }
  352. return 0;
  353. }
  354. auto pWindow::statusBarReposition() -> void {
  355. @autoreleasepool {
  356. NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]];
  357. [[cocoaWindow statusBar] setFrame:NSMakeRect(0, 0, area.size.width, statusBarHeight())];
  358. [[cocoaWindow contentView] setNeedsDisplay:YES];
  359. }
  360. }
  361. auto pWindow::_append(mWidget& widget) -> void {
  362. @autoreleasepool {
  363. if(auto pWidget = widget.self()) {
  364. [pWidget->cocoaView removeFromSuperview];
  365. [[cocoaWindow contentView] addSubview:pWidget->cocoaView positioned:NSWindowAbove relativeTo:nil];
  366. pWidget->setGeometry(widget.geometry());
  367. [[cocoaWindow contentView] setNeedsDisplay:YES];
  368. }
  369. }
  370. }
  371. auto pWindow::_geometry() -> Geometry {
  372. @autoreleasepool {
  373. NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]];
  374. area.size.height -= statusBarHeight();
  375. return {
  376. (int)area.origin.x, (int)(Monitor::geometry(Monitor::primary()).height() - area.origin.y - area.size.height),
  377. (int)area.size.width, (int)area.size.height
  378. };
  379. }
  380. }
  381. }
  382. #endif