MacOS- Can't open a NSWindow in command line app - macos

I use Xcode to create an command line app for MacOS.And I want to open a NSWindow,However,the nswindow failed to display.Is this possible?
Here's the demo code.
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
}
NSRect frame = NSMakeRect(0, 0, 300, 300);
NSUInteger styleMask = NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable|NSWindowStyleMaskMiniaturizable;
NSRect rect = [NSWindow contentRectForFrameRect:frame styleMask:styleMask];
NSWindow *nswindow = [[NSWindow alloc] initWithContentRect:rect styleMask:styleMask backing: NSBackingStoreBuffered defer:false];
[nswindow center];
[nswindow setBackgroundColor:[NSColor whiteColor]];
[nswindow orderFrontRegardless];
return 0;
}

Related

OSX Cocoa cursor in resources

ALL,
On Windows cursors are indicated by the cur file extension. Is there something similar for OSX Cocoa application?
Is there a way in XCode to edit such files, just like in MSVC there is some basic graphical editor?
Also, is there a special API which will allow loading such files on Cocoa? Or I can just load them as images?
TIA!!
EDIT:
In the meantime I think I found a good solution that will satisfy my needs
NSString *path;
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:cursor_file ofType:#"cur"];
if( path )
{
CGImageSourceRef image = CGImageSourceCreateWithURL( path, nil );
CFDictionaryRef properties =
CGImageSourceCopyPropertiesAtIndex( image, 0, nil );
NSInteger x = properties["hotspotX"];
NSInteger y = properties["hotspotY"];
}
Except that it doesn't compile:
I need to import the file where CGImageSourceCreateWithURL() is declared, and I need to fix the last 2 lines, because the array subscript can't be strings.
Could someone help, please?
The following source code will create a custom NSCursor in Xcode from a .cur image, after retrieving its “hotspot” coordinates from the file located in the app bundle Resource folder. Replace the contents of the main.m file with the code below and delete the pre-supplied AppDelegate files to avoid duplicate symbols. You will need to copy/paste the .cur file into the Xcode project folder and also drag and drop it into the Project navigator. Xcode will not automatically place this file into the Resources folder of your application bundle (unlike a .png file), so copy/paste it there as well after you have compiled the app. Reference: Hotspot in Windows cursor .cur loaded by NSImage?
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
CGFloat x,y;
}
#end
#implementation AppDelegate
- (void) customCursor {
// **** Get HotSpot from .cur file **** //
NSBundle *bundle = [NSBundle mainBundle];
NSURL *url = [bundle URLForImageResource:#"myCursor.cur"];
if( url ) {
CGImageSourceRef image = CGImageSourceCreateWithURL( (__bridge CFURLRef)url, nil );
NSDictionary *properties = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex( image, 0, nil );
x = [[properties objectForKey:#"hotspotX"]floatValue];
y = [[properties objectForKey:#"hotspotY"]floatValue];
CFBridgingRelease(image);
CFBridgingRelease((__bridge CFTypeRef _Nullable)(properties));
}
NSCursor *customCursor = [[NSCursor alloc] initWithImage:[NSImage imageNamed:#"myCursor.cur"] hotSpot: NSMakePoint( x, y) ];
[customCursor set];
NSLog(#"x = %0.02f",x);
NSLog(#"y = %0.02f",y);
}
- (void) arrowCursor {
[[NSCursor arrowCursor] set];
}
- (void) buildMenu {
NSMenu *menubar = [NSMenu new];
[NSApp setMainMenu:menubar];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
NSMenu *appMenu = [NSMenu new];
[menuBarItem setSubmenu:appMenu];
[appMenu addItemWithTitle:#"Quit" action:#selector(terminate:) keyEquivalent:#"q"];
}
- (void) buildWnd {
#define _wndW 500
#define _wndH 250
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH ) styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"Custom cursor"];
[window makeKeyAndOrderFront: nil];
// **** Button 1 **** //
NSButton *myBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 30, 30, 135, 30 )];
[myBtn setBezelStyle:NSBezelStyleRounded ];
[myBtn setTitle: #"Custom Cursor"];
[myBtn setAction: #selector (customCursor)];
[[window contentView] addSubview: myBtn];
// **** Button 2 **** //
NSButton *myBtn2 =[[NSButton alloc]initWithFrame:NSMakeRect( 190, 30, 135, 30 )];
[myBtn2 setBezelStyle:NSBezelStyleRounded ];
[myBtn2 setTitle: #"Arrow Cursor"];
[myBtn2 setAction: #selector (arrowCursor)];
[[window contentView] addSubview: myBtn2];
// **** Quit btn **** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWnd];
}
#end
int main() {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
return 0;
}

Title Bar Transparency Removes Blur

I have a subclassed NSWindow which uses the undocumented CGSSetWindowBackgroundBlurRadius method to blur a transparent background.
This is working fine however I also want to blur the title bar. In order to do this I can OR a NSFullSizeContentViewWindowMask mask to the existing style. This successfully changes the title bar to a transparent view but the blur effect is lost! Any ideas of how I can go about this?
#import "TransparentNSWindow.h"
#implementation TransparentNSWindow
typedef void * CGSConnection;
extern OSStatus CGSSetWindowBackgroundBlurRadius(CGSConnection connection, NSInteger windowNumber, int radius);
extern CGSConnection CGSDefaultConnectionForThread();
- (void)enableBlurForWindow:(NSWindow *)window
{
[window setOpaque:NO];
window.backgroundColor = [NSColor colorWithCalibratedRed:255./255. green:255./255. blue:255./255. alpha:0.4];
CGSConnection connection = CGSDefaultConnectionForThread();
CGSSetWindowBackgroundBlurRadius(connection, [window windowNumber], 20);
}
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag {
NSUInteger currentStyle = [self styleMask];
NSUInteger style = NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask;
self = [super initWithContentRect:contentRect styleMask :style backing :NSBackingStoreBuffered defer:NO ];
if (self)
{
[self setOpaque:NO];
[self setHasShadow:NO];
self.titlebarAppearsTransparent = true;
self.titleVisibility = true;
// Uncommenting this line results in a transparent title bar but no blur
//self.styleMask |= NSFullSizeContentViewWindowMask;
[self enableBlurForWindow:self];
}
return self;
}
#end
Got in touch with Apple and apparently there is no way that this can be done.

How to receive NSNotifications while manually using NSRunloop

I can observe NSWorkspaceWillPowerOffNotification notifications in the application delegate of a GUI program, but not in a command line utility. Any ideas why the following does not work? Or is there a different approach I should consider?
Edit: Cold it be that NSWorkspace's notification center doesn't work for a GUI-less program? (I have verified that |notificationCenter| is not nil)
#interface Helper : NSObject
-(void)userLogout:(NSNotification*)notification;
#end
#implementation Helper
-(void)userLogout:(NSNotification*)notification
{
NSLog(#"Notification Received");
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
Helper* helper = [[Helper alloc] init];
NSNotificationCenter* notificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
[notificationCenter addObserver:helper
selector:#selector(userLogout:)
name:NSWorkspaceWillPowerOffNotification
object:nil];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
[runLoop run];
}
return 0;
}

OpenGL context with Cocoa (OS X)

I am trying to programmatically create a Cocoa window with an OpenGL context for an OS X application. I have been unable to find an example online which does not use the Interface Builder for creating a window and OpenGL view.
All I want is for glClear to make my window magenta (0xFF00FF). However, the window remains white.
Here is my project:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
NSOpenGLContext *openGLContext;
}
#property (assign) NSWindow *window;
#property (assign) NSOpenGLContext *openGLContext;
- (void)draw;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window;
#synthesize openGLContext;
static NSOpenGLPixelFormatAttribute glAttributes[] = {
0
};
- (void)draw {
NSLog(#"Drawing...");
[self.openGLContext makeCurrentContext];
glClearColor(1, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
[self.openGLContext flushBuffer];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSRect frame = NSMakeRect(0, 0, 200, 200);
self.window = [[[NSWindow alloc]
initWithContentRect:frame
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO] autorelease];
[self.window makeKeyAndOrderFront:nil];
NSOpenGLPixelFormat *pixelFormat
= [[NSOpenGLPixelFormat alloc] initWithAttributes:glAttributes];
self.openGLContext = [[NSOpenGLContext alloc]
initWithFormat:pixelFormat shareContext:nil];
[self.openGLContext setView:[self.window contentView]];
[NSTimer
scheduledTimerWithTimeInterval:.1
target:self
selector:#selector(draw)
userInfo:nil
repeats:YES];
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)_app {
return YES;
}
#end
main.m
#import "AppDelegate.h"
#import <Cocoa/Cocoa.h>
int main(int argc, char **argv) {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
return NSApplicationMain(argc, (const char **) argv);
}
The documentation for -[NSOpenGLContext flushBuffer] says:
Discussion
If the receiver is not a double-buffered context, this call does nothing.
You can cause your context to be double-buffered by including NSOpenGLPFADoubleBuffer in your pixel format attributes. Alternatively, you can call glFlush() instead of -[NSOpenGLContext flushBuffer] and leave your context single-buffered.
Use this code for the pixel format attributes instead.
NSOpenGLPixelFormatAttribute glAttributes[] =
{
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
0
};
I've tested it and you get the magenta screen you were looking for.

Can't get NSMouseMoved events from nextEventMatchingMask with an NSOpenGLView

I'm trying to create a basic opengl window with mouse input using a typical win-style event loop. The problem is that I'm pulling my hair out trying to get a NSMouseMoved event to generate. The following code outputs debug info on Mouse Up, Mouse Down, Mouse Drag, etc but no Mouse Move even though I've told the window setAcceptsMouseMovedEvents:YES. So, any ideas on how to get mouse move to work in the following example?
Obviously, the way I'm creating the window is very un-cocoa like, but I'm trying to port a makefile-based windows c++ codebase that does some tricky threading things. That's why I'm sticking with a style similar to a win32 loop using GetMsg().
Also, to build I'm just using:
gcc -o hellogl hellogl.m -framework Foundation -framework Cocoa -framework OpenGL
Thanks for the help!
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#include <OpenGL/gl.h>
#include <stdlib.h>
#interface BaseWinDelegate : NSWindow<NSWindowDelegate>
#end
#implementation BaseWinDelegate
- (void) windowWillClose:(NSNotification*)notification
{
printf("Closing.\n");
NSEvent * evt = [NSEvent otherEventWithType:NSApplicationDefined
location: NSMakePoint(0,0)
modifierFlags: 0
timestamp: 0.0
windowNumber: 0
context: nil
subtype: 0
data1: 0
data2: 0];
[NSApp postEvent:evt atStart:NO];
}
#end
#interface BaseView : NSOpenGLView
- (void) update;
- (void) drawRect:(NSRect)rect;
- (void) reshape;
#end
#implementation BaseView
- (void) drawRect:(NSRect)rect
{
glClearColor(0.2f,0.2f,0.2f,0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[[self openGLContext] flushBuffer];
}
- (void) update
{
printf("Update.\n");
}
- (void) reshape
{
NSRect rect;
[[self openGLContext] update];
rect = [self bounds];
printf("Reshape - %f, %f\n", rect.size.width,rect.size.height);
}
#end
int main(int argc, const char * argv[])
{
printf("Starting.\n");
NSAutoreleasePool * myPool = [[NSAutoreleasePool alloc] init ];
NSApplicationLoad();
NSRect rect = NSMakeRect(100,100,640,480);
NSWindow * win = [[NSWindow alloc] initWithContentRect:rect
styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask
backing: NSBackingStoreBuffered
defer: NO];
NSOpenGLPixelFormatAttribute attributes[] =
{
NSOpenGLPFADoubleBuffer,
0
};
NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
BaseView * pView = [[BaseView alloc] initWithFrame:rect pixelFormat:pf];
BaseWinDelegate * myDelegate = [BaseWinDelegate alloc];
[win setDelegate:myDelegate];
[win setContentView: pView];
[win makeKeyAndOrderFront: NSApp];
[win setAcceptsMouseMovedEvents:YES];
do
{
NSEvent * evt = [NSApp nextEventMatchingMask : NSAnyEventMask
untilDate : [NSDate distantFuture]
inMode : NSDefaultRunLoopMode
dequeue : YES ];
NSEventType evtType = [evt type];
if (evtType == NSApplicationDefined)
{
break;
}
printf("%d\n",(int)evtType);
[NSApp sendEvent : evt];
} while (1);
[myPool drain];
return EXIT_SUCCESS;
}
Ok, figured it out. The problem is that it's not a foreground process. So adding this code fixes it.
ProcessSerialNumber psn;
GetCurrentProcess(&psn);
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
That was preventing the window from becoming the key window and sending off mouse moved events. Hope it helps someone else (i.e. the 0.1% of people who like to handle their own event loop).

Resources