Results 1 to 7 of 7

Thread: HOWTO: Developing Plugins

  1. #1
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867

    HOWTO: Developing Plugins

    Here I will hopefully explain how to build a plugin for CarFrontEnd

    For anyone that wants to make a plugin, PLEASE let me know and keep me posted on how you are doing and what help/changes you need. I will be glad to support anyone that wants to take a crack at it.

    First you will need to include the CarFrontEndAPI framework in your project. Then you need to import the "CarFrontendAPI/CarFrontEndAPI.h" header file, you need to make sure your plugins extension is ".cfep" (CarFrontEnd Plugin), and finally you need to make your main object conform to the CarFrontEndProtocol protocol.

    If you want to use my buttons (I still need to fix those images), you need to use a Bezel Button and set it's custom class to "CarFrontEndButton" (import CarFrontEndButton.h from CarFrontEndAPI). I will get around to a IB palette at some point...

    The CarFrontEndAPI.h imports the Protocol's header file:
    Code:
    /*
     * CarFrontEndAPI - CarFrontEndProtocol.h - David Whittle (iamgnat@gmail.com)
     * Copyright (C) 2007  David Whittle (iamgnat@gmail.com)
     * 
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     * 
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     * 
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    #import <Foundation/Foundation.h>
    
    @protocol CarFrontEndProtocol <NSObject>
    
    // The init method that the PluginManager will call.
    - (id) initWithPluginManager: (id) pluginManager;
    
    // Return the name of the plugin.
    - (NSString *) name;
    
    // Allow the plugin to perform any startup initialization that may be desired
    //  This is called shortly after the plugin is loaded, but is guaranteed to be
    //  called before the first call to pluginButton or contentViewForSize:.
    //  This will be called regardless of if the plugin is ever used, so care
    //  should be taken to only perform work that is absolutely required.
    - (void) initalize;
    
    // Return the image to be used for the for the plugin buttons.
    //  The current size is 119x45, but this is subject to change so make sure
    //  your image will scale up or down effectively.
    //  Please cache this data where possible as it will be called as this will
    //  be called on a frequent basis (0.1 - 0.2 seconds). This allows you to
    //  perform rough animations (e.g. CPU monitor, etc..)
    - (NSImage *) pluginButtonImage;
    
    // Return the view to be used for the content view.
    //  The client will pass the view size so that you may supply different
    //  views based on the size. The client will resize the supplied view
    //  before it is displayed.
    - (NSView *) contentViewForSize: (NSSize) size;
    
    // Allow the plugin to perform actions when it's view is replaced.
    //  Ideally you should stop all processing that is not needed (e.g. UI updates,
    //  etc..), but obviously some cases will not fit this scenario.
    - (void) removePluginFromView;
    
    @end
    The notes in the header hopefully give you a good enough idea of what you need to/can do.

    Here are the source files for the SamplePlugin plugin. Hopefully they will show you the gory details of what needs to be done. These two files represent all the code for the plugin.

    SamplePlugin.h:
    Code:
    /*
     * SamplePlugin - SamplePlugin.h - gnat (iamgnat@gmail.com)
     * Copyright (C) 2007  gnat (iamgnat@gmail.com)
     * 
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     * 
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     * 
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    #import <Cocoa/Cocoa.h>
    #import <CarFrontEndAPI/CarFrontEndAPI.h>
    
    @interface SamplePlugin : NSObject <CarFrontEndProtocol> {
        id                  owner;
        IBOutlet NSView     *samplePluginView;
    }
    
    - (id) initWithPluginManager: (id) pluginManager;
    - (NSString *) name;
    - (void) initalize;
    - (NSImage *) pluginButtonImage;
    - (NSView *) contentViewForSize: (NSSize) size;
    - (void) removePluginFromView;
    
    - (IBAction) buttonClicked: (id) sender;
    
    @end
    SamplePlugin.m:
    Code:
    /*
     * SamplePlugin - SamplePlugin.m - gnat (iamgnat@gmail.com)
     * Copyright (C) 2007  gnat (iamgnat@gmail.com)
     * 
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     * 
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     * 
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    #import "SamplePlugin.h"
    
    static SamplePlugin *sharedSP = nil;
    
    @implementation SamplePlugin
    
    - (id) init {
        return([self initWithPluginManager:nil]);
    }
    
    - (id) initWithPluginManager: (id) pluginManager {
        if (sharedSP != nil) {
            [self release];
            return(sharedSP);
        }
        
        [super init];
        owner = [pluginManager retain];
        
        // Setup for a single instance.
        sharedSP = self;
        
        return(self);
    }
    
    - (NSString *) name {
        return(@"Sample Plugin");
    }
    
    - (void) initalize {
        // No-op for this example.
        //  Should generate the button image here rather than on demand.
    }
    
    - (NSImage *) pluginButtonImage {
    	NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
        
        [attributes setObject:[NSFont fontWithName:@"Helvetica" size:26]
                       forKey:NSFontAttributeName];
    	[attributes setObject:[NSColor whiteColor]
                       forKey:NSForegroundColorAttributeName];
        
        NSSize          size = [[self name] sizeWithAttributes:attributes];
        NSImage         *image = [[[NSImage alloc] initWithSize:size] autorelease];
        
        [image lockFocus];
        [[self name] drawAtPoint:NSZeroPoint withAttributes:attributes];
        [image unlockFocus];
        
        return(image);
    }
    
    - (NSView *) contentViewForSize: (NSSize) size {
        // We are ignoring the size value, but it is there incase you have differnt
        //  views based on the size that CarFrontEnd sends you.
        if (samplePluginView == nil) {
            [NSBundle loadNibNamed:@"SamplePlugin" owner:self];
        }
        
        return(samplePluginView);
    }
    
    - (void) removePluginFromView {
        // No-op
        //  If you need to do something when your view is no longer displayed,
        //  add the code here.
    }
    
    - (IBAction) buttonClicked: (id) sender {
        if ([[sender stringValue] isEqualToString:@"Click"]) {
            [sender setStringValue:@"Clock"];
        } else if ([[sender stringValue] isEqualToString:@"Clock"]) {
            [sender setStringValue:@"Click"];
        }
    }
    
    @end
    Ask away

    -dave
    My pathetic worklog.
    CarFrontEnd (now it's own sub-forum!!!!)

  2. #2
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867
    I've done some documentation



    I have added a Wiki page to the project that hopefully is a good guide to creating a basic plugin for CarFrontEnd. I also added SamplePlugin to the source tree in the Plugins directory which is the example the documentation is based on (I figured it was about time that I actually attempt to make a stand alone plugin ).

    If anyone tries to follow the directions and has issues, PLEASE let me know so I can both help you out and update the docs.

    -dave

    EDIT: Changed the sample source in the first post from the iTunesMusicPlayer source to the SamplePlugin source for simplicity.
    My pathetic worklog.
    CarFrontEnd (now it's own sub-forum!!!!)

  3. #3
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867

    Please use the latest Xcode

    If you are getting strange errors (like it can't find a header that you know is there), make sure you are running the latest version of Xcode (for 10.4 as of right now).

    I was helping someone with a plugin where it couldn't find CarFrontEndAPI.h even though we confirmed it was loaded in the project correctly and it was where it was supposed to be. In further testing (a simple CLI Obj-C program) we got a strange linker error about an "unknown cmd field" which apparently typically shows up if you upgrade the OS (major versions were the examples I found) without updating Xcode. Even though they only ever used 10.4, it was an old version of Xcode. Once it was updated to the latest and greatest, both the test program and the plugin built just fine.

    If you are having odd build problems (unable to locate CarFrontEndAPI.h, strange linker errors against CarFrontEndAPI, etc..), here is a simple test you can use to help determine if it is a System or Project issue:

    1. Create cfe_test.m with the following contents (if you aren't familiar with Terminal/Unix, save the file in your home directory):
      Code:
      #import <Foundation/Foundation.h>
      #import <Cocoa/Cocoa.h>
      #import <CarFrontEndAPI/CarFrontEndAPI.h>
      
      int main (int c, char *argv[]) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        NSLog(@"Hello");
        [pool release];
        return(0);
      }
    2. Open Terminal.app (Applications->Utilities->Terminal) and go to the directory where you saved the file (if you saved it to your home directory, you are already there).
    3. Run the following to compile the test:
      Code:
      gcc cfe_test.m -o cfe_test -framework Foundation -framework CarFrontEndAPI -framework Cocoa

    That should compile the test with no messages. If there are warnings or errors, then something is not right with either the CarFrontEndAPI installation or your development environment.
    If you do get errors, verify that you are using the latest Xcode (possibly reinstall just incase something got corrupted) and reinstall CarFrontEnd. If neither of those options solves the issue, ping me and i'll be glad to muddle through trying to help as best I can.

    -dave
    My pathetic worklog.
    CarFrontEnd (now it's own sub-forum!!!!)

  4. #4
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867
    The 1.0a3 release adds a title bar to the CarFrontEnd application window if you build CarFrontEnd in Debug mode. This should make it easier to debug your plugins.

    -dave
    My pathetic worklog.
    CarFrontEnd (now it's own sub-forum!!!!)

  5. #5
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867
    The 1.0a7 release adds messaging support to enable plugins to communicate with one another and with CarFrontEnd itself.

    Here are the supported protocols that the PluginManager (initWithPluginManager will respond to:

    Code:
    /*
     * CarFrontEndAPI - CarFrontEndProtocol.h - David Whittle (iamgnat@gmail.com)
     * Copyright (C) 2007  David Whittle (iamgnat@gmail.com)
     * 
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     * 
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     * 
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    #import <Foundation/Foundation.h>
    #import <CarFrontEndAPI/CarFrontEndAPI.h>
    
    #pragma mark Plugin message utility methods
    @protocol PluginMessaging
    
    // Register a method to respond to a given message.
    - (void) addObserver: (id) object selector: (SEL) selector
                    name: (CFEMessage) message;
    
    // Unregister your method for a message.
    - (void) removeObserver: (id) object name: (CFEMessage) message;
    
    // Unregister the object for all messages it listens for.
    - (void) removeAllObserversFor: (id) object;
    
    // Send a message for other plugins or CFE to respond to.
    - (void) sendMessage: (CFEMessage) message withObject: (id) userInfo;
    
    @end
    
    #pragma mark Plugin management utilities.
    @protocol PluginPlugins
    
    // Returns an array of plugin names.
    - (NSArray *) plugins;
    
    // Takes the index of a plugin (as returned by -plugins) and loads it's
    //  content view.
    - (void) loadViewForPlugin: (int) pluginIndex;
    
    // Load the contentView of the plugin associated to the first Quick Slot.
    - (void) quickSlot1;
    
    // Load the contentView of the plugin associated to the second Quick Slot if
    //  applicable.
    - (void) quickSlot2;
    
    // Load the contentView of the plugin associated to the third Quick Slot if
    //  applicable.
    - (void) quickSlot3;
    
    @end
    
    #pragma mark Plugin generic utility methods
    @protocol PluginUtilities
    
    // Return a new window object configured to meet the basic CFE standards.
    - (NSWindow *) windowWithContentRect: (NSRect) frame;
    
    @end
    
    #pragma mark Plugin CarFrontEnd utility methods
    @protocol PluginCarFrontEnd
    
    // Returns the current volume level.
    - (NSNumber *) currentVolumeLevel;
    
    // Returns (left/right) the current side considered the driver's side.
    - (NSString *) currentDriverSide;
    
    @end
    
    #pragma mark Plugin Preferences methods
    @protocol PluginPreferences
    
    // Returns the current preferences for the given plugin.
    //  This is based on the value returned by the -name method of the plugin, so
    //      it is critical not to share names with other plugins.
    - (NSDictionary *) preferencesForPlugin: (id <CarFrontEndProtocol>) plugin;
    
    // Stores the given preferences dictionary.
    //  The structure of the dictionary is completely arbitrary from CFE's point
    //      of view and completely in the Plugin's control.
    //  This is based on the value returned by the -name method of the plugin, so
    //      it is critical not to share names with other plugins.
    - (void) savePreferences: (NSDictionary *) pluginPreferences
                  forPlugin: (id <CarFrontEndProtocol>) plugin;
    
    @end
    Here is a list of the currently support messages.

    Code:
    #pragma mark CarFrontEnd Volume Messages
    // Toggles the current mute setting.
    //  CarFrontEnd will always respond to this message.
    //  Any userInfo object will be ignored.
    extern CFEMessage   CFEMessageVolumeMute;
    
    // Set the volume level to the given value.
    //  CarFrontEnd will always respond to this message.
    //  The userInfo object should be a NSNumber with a value between 0 and 100.
    //  If userInfo does not respond to intValue or the value is outside of the
    //      0 to 100 range, no volume change will occur.
    extern CFEMessage   CFEMessageVolumeSet;
    
    // Sent when the volume level is changed.
    //  It is expected for plugins that are interested in this information will
    //      add an observer for it.
    //  The current volume level will be passed as a NSNumber in the userInfo.
    extern CFEMessage   CFEMessageVolumeChanged;
    
    #pragma mark CarFrontEnd Menu Messages
    // Causes the Menu content to be displayed.
    //  CarFrontEnd will always respond to this message.
    //  Any userInfo value will be ignored.
    extern CFEMessage   CFEMessageMenuShowView;
    
    // Causes CarFrontEnd to be hidden.
    //  CarFrontEnd will always respond to this message.
    //  Any userInfo value will be ignored.
    extern CFEMessage   CFEMessageMenuHideApp;
    
    // Causes CarFrontEnd to exit.
    //  CarFrontEnd will always respond to this message.
    //  Any userInfo value will be ignored.
    extern CFEMessage   CFEMessageMenuQuitApp;
    
    // Causes CarFrontEnd to swap the driver side display.
    //  CarFrontEnd will always respond to this message.
    //  If userInfo does not respond to isEqualToString, is not "left" or "right",
    //      or is nil, then it will swap based on the current setting.
    //      If it does it will swap to the given side if that is not the current
    //      side.
    extern CFEMessage   CFEMessageMenuSwapSide;
    
    // Sent when the driver's side is changed.
    //  It is expected for plugins that are interested in this information will
    //      add an observer for it.
    //  The current side (left/right) will be passed as a NSString in the userInfo.
    extern CFEMessage   CFEMessageMenuSideSwapped;
    More details about using the messaging system can be found in the Wiki.

    -dave
    My pathetic worklog.
    CarFrontEnd (now it's own sub-forum!!!!)

  6. #6
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867

    1.0a9 Plugin API additions

    The 1.0a9 release adds a new method you can call from the PluginManager, and two new optional methods your plugin can support.

    Firstly I have added - (NSWindow *) mainWindow to allow you to get the pointer to the main CarFrontEnd plugin. Since the -mainWindow of NSApplication method does not work in full screen mode (actually it's the process of designating the main window vs trying to retrieve it), I have added this method so that you can get the handle to the primary window even if your NSView is currently not in it (e.g. in your -contentViewForSize: method).

    Next I have added support for two additional methods to let your plugin know more about where we are in the process of display or removing your view from the content area.

    The first is - (void) viewWasMadeVisible which will be sent to your plugin (if you've defined it) after your NSView (from -contentViewForSize:) has been made the current content.

    The second is - (BOOL) viewWillBeRemovedFromView which will be sent just before your view is removed from the content area. Remember that the -removePluginFromView happens after the view has already been removed.

    Let me know if you have any questions.

    -dave
    My pathetic worklog.
    CarFrontEnd (now it's own sub-forum!!!!)

  7. #7
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867
    This is a bump to hold this thread near the top for the time being, but i'm unsticking this thread since i'm doing wholesale changes to how plugins work and i'm not keeping backwards compatibility.

    As stated in my State of the Code thread, I will help (or do it all) convert plugins using this architecture that are built and released (open source) prior to my first release of the new code.

    -dave
    My pathetic worklog.
    CarFrontEnd (now it's own sub-forum!!!!)

Similar Threads

  1. Replies: 15
    Last Post: 08-21-2007, 03:46 PM
  2. VST plugins in streetdeck?
    By cougarz in forum DigitalMods (Scripts / API)
    Replies: 5
    Last Post: 01-23-2007, 02:24 PM
  3. is freedrive developing dead?
    By Nomader in forum FreeDrive
    Replies: 4
    Last Post: 08-04-2006, 09:59 AM
  4. Embed several plugins, time delay
    By Flappy in forum Road Runner
    Replies: 3
    Last Post: 01-02-2006, 03:22 PM
  5. Codecs and plugins
    By Rider in forum Road Runner
    Replies: 3
    Last Post: 09-20-2005, 08:55 AM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •