Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cocoa: programmatically show/hide custom NSWindow with custom NSView

First of all I have to inform you that I am new to Objective-C and Cocoa.

I have read some books about that and I am able to build quite simple programmes now.

it's been 15 days that I'm stuck with a program that I'm trying to build and I really don't know more where to look..

I want to build a program that can change the brightness of my monitor using DDC/CI and I want to show/hide a window (where it is written the level of brightness) exactly like the Apple brightness bezel of Leopard or Snow Leopard with the exact same style.

Using RegisterEventHotKey and various IOservices functions I have been able to increase brightness when I press F2 and to decrease it wheh I press F1.

Using custom NSWindow (TransparentWindow) and custom NSView (RoundedView) I have been able to obtain a window which appears exactly as the Apple brightness bezel. I have put it on awakeFromNib and it appears correctly and stays there.

What I am not able to achieve (and I'm really becoming crazy on that) is to show the window only when I press F1 or F2. (to hide it I have alredy implemented an NSTimer but this is offtopic now)

I tried different ways:

1) From the NSobject class where I implement RegisterEventHotKey I have created an instance of TransparentWindow and then I have sent orderOut to that instance.

2) I have used NSNotificationCenter to send a notification directly to TransparentWindow class and call orderOut from there.

3) and many other that I don't remember now.

What I'm trying to do now is to make the window appearing by creating it inside awakeFromNib (and this works) and then to hide it with orderOut (and this never works).

These are the involved classes:

TransparentWindow.h:

#import <Cocoa/Cocoa.h>

@interface TransparentWindow : NSWindow
{
IBOutlet NSWindow *window;
}

@property (retain) IBOutlet NSWindow *window;

@end

TransparentWindow.m:

#import "TransparentWindow.h"


@implementation TransparentWindow

@synthesize window;


- (id)initWithContentRect:(NSRect)contentRect 
                styleMask:(unsigned int)aStyle 
                  backing:(NSBackingStoreType)bufferingType 
                    defer:(BOOL)flag {

    window = [super initWithContentRect:contentRect 
                                        styleMask:NSBorderlessWindowMask 
                                          backing:NSBackingStoreBuffered 
                                defer:NO];
    if (window != nil) {

        [window setLevel: NSStatusWindowLevel];
        [window setBackgroundColor: [NSColor clearColor]];
        [window setAlphaValue:1.0];
        [window setOpaque:NO];
        [window setHasShadow:NO];
        [window setIgnoresMouseEvents: YES];
        NSLog(@"%p", window);

    }
    return window;

}


- (BOOL) canBecomeKeyWindow
{
    return YES;
}

- (void)notify {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotification" object:self];
}




- (void)handleNotification:(NSNotification*)note {

        [window orderOut:self];

    }


@end

RoundedView.h:

#import <Cocoa/Cocoa.h>

@interface RoundedView : NSView
{
}


@end

RoundedView.m:

#import "RoundedView.h"

@implementation RoundedView


- (void)drawRect:(NSRect)rect
{
        NSColor *bgColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.25];
    NSRect bgRect = rect;
    int minX = NSMinX(bgRect);
    int midX = NSMidX(bgRect);
    int maxX = NSMaxX(bgRect);
    int minY = NSMinY(bgRect);
    int midY = NSMidY(bgRect);
    int maxY = NSMaxY(bgRect);
    float radius = 20.0; // correct value to duplicate Panther's App Switcher
    NSBezierPath *bgPath = [NSBezierPath bezierPath];

    // Bottom edge and bottom-right curve
    [bgPath moveToPoint:NSMakePoint(midX, minY)];
    [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY) 
                                     toPoint:NSMakePoint(maxX, midY) 
                                      radius:radius];

    // Right edge and top-right curve
    [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, maxY) 
                                     toPoint:NSMakePoint(midX, maxY) 
                                      radius:radius];

    // Top edge and top-left curve
    [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY) 
                                     toPoint:NSMakePoint(minX, midY) 
                                      radius:radius];

    // Left edge and bottom-left curve
    [bgPath appendBezierPathWithArcFromPoint:bgRect.origin 
                                     toPoint:NSMakePoint(midX, minY) 
                                      radius:radius];
    [bgPath closePath];

    [bgColor set];
    [bgPath fill];

}

MainProgram.h:

#import <Cocoa/Cocoa.h>
#import <carbon/carbon.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <IOKit/IOKitLib.h> 
#include <ApplicationServices/ApplicationServices.h>
#include <IOKit/i2c/IOI2CInterface.h>

@interface MainProgram : NSObject {

}

@end

MainProgram.m:

#import "MainProgram.h"
#import <Carbon/Carbon.h>
#import "TransparentWindow.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <IOKit/IOKitLib.h> 
#include <ApplicationServices/ApplicationServices.h>
#include <IOKit/i2c/IOI2CInterface.h>


static OSStatus OnHotKeyEvent(EventHandlerCallRef nextHandler,EventRef theEvent,void *userData);

extern int level;

@implementation MainProgram


- (void)awakeFromNib
{

    EventHotKeyRef gMyHotKeyRef;
    EventHotKeyID gMyHotKeyID;
    EventTypeSpec eventType;
    eventType.eventClass=kEventClassKeyboard;
    eventType.eventKind=kEventHotKeyPressed;

    InstallApplicationEventHandler(&OnHotKeyEvent, 1, &eventType, NULL, NULL);

    gMyHotKeyID.signature='htk1';
    gMyHotKeyID.id=1;
    //RegisterEventHotKey(122, controlKey, gMyHotKeyID, GetApplicationEventTarget(), 0, &gMyHotKeyRef);
    RegisterEventHotKey(122, NULL, gMyHotKeyID, GetApplicationEventTarget(), 0, &gMyHotKeyRef);

    gMyHotKeyID.signature='htk2';
    gMyHotKeyID.id=2;
    RegisterEventHotKey(120, NULL, gMyHotKeyID, GetApplicationEventTarget(), 0, &gMyHotKeyRef);
}




OSStatus OnHotKeyEvent(EventHandlerCallRef nextHandler,EventRef theEvent,
                         void *userData)
{

    EventHotKeyID hkCom;
    GetEventParameter(theEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,
                      sizeof(hkCom),NULL,&hkCom);
    int l = hkCom.id;

    switch (l) {
        case 1: 
            //NSLog(@"Hotkey 1");
            level = level - 5;

            if (level<0) {
                level = 0;
            }
            else {
                BrightnessMain(level);
            }
            break;
        case 2: 
            //NSLog(@"Hotkey 2");
            level = level + 5;

            if (level>100) {
                level = 100;
            }
            else {
                BrightnessMain(level);

                TransparentWindow *object3 = [[TransparentWindow alloc] init];

                //[object3 orderOut:nil];
                [[NSNotificationCenter defaultCenter] addObserver:object3 selector:@selector(handleNotification:) name:@"MyNotification" object:nil];
                [object3 notify];

            }
            break;
    }

    return noErr;
}


@end

Where is the mistake? I'm not an expert programmer but I think it could be a stupid simple mistake but I'm not able to figure it out..

Update of MainProgram.m:

I have replaced this

TransparentWindow *object3 = [[TransparentWindow alloc] init];

with this:

NSRect contentRect;
NSSize contentSize;

contentSize.width  = 210;
contentSize.height = 205;

contentRect.origin = NSMakePoint(855, 140);
contentRect.size  = contentSize;

TransparentWindow *object3 = [[TransparentWindow alloc] initWithContentRect:contentRect 
                                                                                  styleMask:NSBorderlessWindowMask 
                                                                                    backing:NSBackingStoreBuffered 
                                                                                      defer:NO];

I'm not sure if this is what you meant..

Corrected TransparentWindow.h:

#import <Cocoa/Cocoa.h>

@interface TransparentWindow : NSWindow
{

}


@end

Corrected TransparentWindow.m:

#import "TransparentWindow.h"


@implementation TransparentWindow


- (id)initWithContentRect:(NSRect)contentRect 
                styleMask:(unsigned int)aStyle 
                  backing:(NSBackingStoreType)bufferingType 
                    defer:(BOOL)flag {

    self = [super initWithContentRect:contentRect 
                                        styleMask:NSBorderlessWindowMask 
                                          backing:NSBackingStoreBuffered 
                                defer:NO];
    if (self != nil) {

        [self setLevel: NSStatusWindowLevel];
        [self setBackgroundColor: [NSColor clearColor]];
        [self setAlphaValue:1.0];
        [self setOpaque:NO];
        [self setHasShadow:NO];
        [self setIgnoresMouseEvents: YES];
        NSLog(@"%p", self);

    }
    return self;

}



- (BOOL) canBecomeKeyWindow
{
    return YES;
}

- (void)notify {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotification" object:self];
}




- (void)handleNotification:(NSNotification*)note {

    [self orderOut:self];

    }

@end

Update 2:

If I use makeKeyAndOrderFront instead of orderFront it happens a really strange fact.

After the first 3 or 4 pressing of the F1 or F2 button, nothing happens but when I press F1 or F2 another time, still no window appears but I get these strange errors in console:

RoundedFloatingPanel[2346] <Warning>: CGSResolveShmemReference : window.RO.dirtyRegion : Reference offset (38464) exceeds bounds (32768) on shmem obj 0x229
RoundedFloatingPanel[2346] <Warning>: CGSResolveShmemReference : window.RO : Reference offset (38144) exceeds bounds (32768) on shmem obj 0x229
RoundedFloatingPanel[2346] <Error>: kCGErrorFailure: CGSNewWindowWithOpaqueShape: Cannot map window information shmem
RoundedFloatingPanel[2346] <Error>: kCGErrorFailure: Set a breakpoint @ CGErrorBreakpoint() to catch errors as they are logged.
2011-02-17 16:01:42.895 RoundedFloatingPanel[2346:a0f] HIToolbox: ignoring exception 'Error (1000) creating CGSWindow' that raised inside Carbon event dispatch
(
    0   CoreFoundation                      0x91f186ba __raiseError + 410
    1   libobjc.A.dylib                     0x999e3509 objc_exception_throw + 56
    2   CoreFoundation                      0x91f183e8 +[NSException raise:format:arguments:] + 136
    3   CoreFoundation                      0x91f1835a +[NSException raise:format:] + 58
    4   AppKit                              0x93408915 _NXCreateWindow + 316
    5   AppKit                              0x93408720 _NSCreateWindow + 59
    6   AppKit                              0x93407946 -[NSWindow _commonAwake] + 1784
    7   AppKit                              0x9340456e -[NSWindow _commonInitFrame:styleMask:backing:defer:] + 1524
    8   AppKit                              0x934031c1 -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1568
    9   AppKit                              0x93402b9b -[NSWindow initWithContentRect:styleMask:backing:defer:] + 71
    10  RoundedFloatingPanel                0x000029a6 -[TransparentWindow initWithContentRect:styleMask:backing:defer:] + 119
    11  RoundedFloatingPanel                0x000031ec OnHotKeyEvent + 447
    12  HIToolbox                           0x95ff0ecf _ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec + 1567
    13  HIToolbox                           0x95ff0196 _ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec + 411
    14  HIToolbox                           0x95fefff5 SendEventToEventTargetWithOptions + 58
    15  HIToolbox                           0x96024c18 _ZL29ToolboxEventDispatcherHandlerP25OpaqueEventHandlerCallRefP14OpaqueEventRefPv + 3006
    16  HIToolbox                           0x95ff1320 _ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec + 2672
    17  HIToolbox                           0x95ff0196 _ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec + 411
    18  HIToolbox                           0x96012a07 SendEventToEventTarget + 52
    19  AppKit                              0x93460c3e -[NSApplication sendEvent:] + 7494
    20  AppKit                              0x933f42a7 -[NSApplication run] + 917
    21  AppKit                              0x933ec2d9 NSApplicationMain + 574
    22  RoundedFloatingPanel                0x00002368 main + 30
    23  RoundedFloatingPanel                0x0000231e start + 54
    24  ???                                 0x00000001 0x0 + 1
like image 736
Andrea3000 Avatar asked Nov 06 '22 02:11

Andrea3000


1 Answers

First,

  TransparentWindow *object3 = [[TransparentWindow alloc] init];

is not calling the initializer you defined in

  @implementation TransparentWindow


  - (id)initWithContentRect:(NSRect)contentRect 
                  styleMask:(unsigned int)aStyle 
                    backing:(NSBackingStoreType)
                      defer:(BOOL)flag { ...

Instead, call

  TransparentWindow *object3 = [[TransparentWindow alloc] initWithContentRect: ...
                                                                    styleMask: ... 
                                                                      backing: ... defer: ...];

with appropriate parameters, so that you call what you defined!

Next, don't synthesize the property window. Your TransparentWindow is a NSWindow because it's a subclass, so you don't have to create NSWindow*window in it. It seems you're mixing window and self inside TransparentWindow, which is very bad.

Remove the property and ivar window, and replace all occurrences of window inside TransparentWindow by just self. For example, the init... method should look like

 self = [super initWithContentRect:contentRect 
                                    styleMask:NSBorderlessWindowMask 
                                      backing:NSBackingStoreBuffered 
                            defer:NO];
 if (self != nil) { ...

Now, please tell me how things go after these changes! I'm not sure it they are enough, but these are most obvious problems.

like image 62
Yuji Avatar answered Nov 13 '22 07:11

Yuji