SBSettings Toggle Spec

Author: BigBoss

v1.5 (October 12, 2011) (Added setContainer)

Want to make your own SBSettings toggle? Know how to develop in objective-C or C? Then this tutorial is for you.

SBSettings is a modular application. All the toggles that are currently out are actually separate coding projects. The settings app, itself, scans the toggles folder located at /var/mobile/Library/SBSettings/Toggles. The toggles are given the name of the folder. The toggles are considered “ON” by default and “OFF” when a file called OFF exists in the toggle’s folder.  The toggles are each treated generically, and are simply dylib files that expose a set of C function calls. This document will explain each of these functions and how they are expected to be implemented.

Rules:
Before getting into all the details, there are some things that should be understood while writing toggles. They are very important so they are covered here:

1) Your toggle will run in springboard. This means if your toggle leaks memory, the system will be leaking memory. If your toggle crashes, the springboard will crash and you’ll get into safe mode. You must be very careful to free (release) all the memory you allocate. It would be good to have another developer code review your code prior to release (I am happy to do so if you wish).

2) Your toggle must not store state or do anything that can hog up memory after the users taps it. This will affect system-wide performance and should not be allowed.

3) Your toggle should not execute processes on disk using popen() or system() functions. These tend to freeze up springboard over time when memory is low, say below 20mb. (I’m not sure why. If someone wants to debug this somehow please do). This means calls to launchctl are pretty much out and have to be handled in another fashion. Note, with SBSettings 3.0 there is a special way to do this. See the section below that describes how to use  sbsettingsd to your advantage.

4) Your toggle will run as user mobile. Many functions require root access.

The functions you must implement in your dylib file:
BOOL isCapable() – This function is called when SpringBoard loads. This function is required. If it does not exist, it is assumed your toggle does not run on the platform and your toggle will not be loaded. If it returns YES, the toggle is assumed to be able to work on the current platform. If NO, then it is not. Here you can check for version of your hardware, or ipod vs iphone. The following code checks if the platform is iPod and if so, returns NO:

uname(&UtsName);
if(strstr(UtsName.machine, “iPod”) != NULL)
{
Capable = NO;
}

BOOL isEnabled() – This function is called when SpringBoard loads and when the refresh button is pressed. This function is required otherwise there is no way to determine if your toggle is on or off.  You need to return YES or NO based on the state of your toggle here.

BOOL getStateFast() – This function is optional. This function is run each time the SBSettings window is opened.  The isEnabled function may take a second or two to determine state. This is not acceptable for performance each time the window is opened. Therefore, isEnabled is only called once when springboard is restarted or resprung or when the refresh button is pressed. getStateFast is called whenever the window is displayed. If your toggle has a way to get a quick state you may implement it here. In some cases this will call isEnabled in your toggle’s implementation code. In others, this will not be implemented. In some cases there maybe be a fast way to get state although it’s not as reliable as isEnabled. This would be a good place to add that code if you consider a “better than nothing” approach better than nothing.

void setContainer(int Container) – This function is optional but you will want this if you require any sort of window to appear (brightness, fast notes, processes, etc). In this function, you will be passed either 0 (sbsettings window) or 1 (notification center). You will need this to determine how to display your window, which is the parent window, etc. SBSettings will call this when your toggle is initialized, prior to calling closeWindow, and prior to calling setState.

void setState(BOOL Enabled) – This function is required. This gets called when the user presses the toggle button. If the toggle state is “ON” then this function gets called with NO passed into the Enabled parameter. If the toggle state is “OFF” then this function gets called with YES passed into the Enabled parameter.

float getDelayTime() - This function is required. This is a time in seconds and partial seconds (example: 1.2f for 1.2 seconds) that it takes for your toggle to perform its “setState” function. It is the amount of time that the spinner will spin on the toggle button after setState function returns.

BOOL allowInCall() – This function is optional. If it is not implemented, it is assumed to return YES. This determines if your toggle can be used while in a call. This is important for a phone toggle or the 3g toggle for example. If used while in call, the call would be dropped.

void invokeHoldAction() – This function is optional. If implemented, the SBSettings button can be held for special options. When the user holds the button, your invokeHoldAction function will be called allowing you to handle the event.

void closeWindow() – If you have a window in your toggle / widget (like brightness), and you want this to be closed when user dismisses sbsettings U/I, implement closeWindow. It will be called when the sbsettings window is about to be closed. It’s called whether your toggle is active or not so make sure you handle the case where your widget window is not active. If you dont have a window, do not implement this function.

How to close sbsettings parent window: If you want your toggle to close sbsettings window, you can obtain the window and call closeButtonPressed on it:

UIWindow* getAppWindow()
{
UIWindow* TheWindow = nil;
UIApplication* App = [UIApplication sharedApplication];
NSArray* windows = [App windows];
int i;
for(i = 0; i < [windows count]; i++)
{
TheWindow = [windows objectAtIndex:i];
if([TheWindow respondsToSelector:@selector(getCurrentTheme)])
{
break;
}
}

if(i == [windows count])
{
TheWindow = [App keyWindow];
}

return TheWindow;
}
UIWindow* Window = getAppWindow();
[Window closeButtonPressed];

Compilation environment:
In theory you can use any compilation environment you wish. I am using the “one click toolchain” which uses old compatibility headers and iphone GCC to compile these on the iPhone, itself. Toggles must be pseudo signed with ldid utility (apt-get install ldid, then ldid -S Toggle.dylib).  Here is a sample makefile that works on iphone gcc with a main.m as your only source file:

— cut here —

CC=arm-apple-darwin-gcc
LD=$(CC)
LDFLAGS=-lobjc -dynamiclib -bind_at_load -F”/System/Library/PrivateFrameworks” -framework CoreTelephony -framework CoreFoundation -framework Foundation -framework UIKit -framework CoreGraphics -framework SystemConfiguration
CFLAGS=-fconstant-cfstrings -std=gnu99 -Wall -O2 -I/var/include -I..
VERSION=1.0

all:    Toggle.dylib

Toggle.dylib: main.o
$(LD) $(LDFLAGS) -o $@ $^
/usr/bin/ldid -S Toggle.dylib

%.o: %.m
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

clean:
rm -f *.o Toggle.dylib

— cut here —

Tips:

  • Remember that your toggle runs in SpringBoard. You can use a class dump of the springboard headers to poke around some functions that can be called.
  • Make no assumptions about functions that exist while getting instances to the current window or the application. Use respondsToSelector constantly.
  • You can get a pointer to the running SpringBoard app (usually) using the standard [UIApplication sharedApplication] class function. This may allow you to use functions in springboard.h. However, make sure you use respondsToSelector as indicated above.
  • If you need to use graphics in your toggle (making a widget) it is advised to use existing graphics where possible so that themes continue to work and do not need to be modified. To do this, remember a few things: 1) Themes are in /var/mobile/Library/SBSettings/Themes and each folder is the theme. 2) You can get the selected theme from the app’s main window using the function getCurrentTheme.  Here is some code used in the brightness toggle to get the current theme from the SBSettings window: (sorry, the editor messed up the formatting of the code)

UIWindow* Window = [self getAppWindow];
if([Window respondsToSelector:@selector(getCurrentTheme)])
{
CurrentTheme = [NSString stringWithString:[Window getCurrentTheme]];
}
else
{
CurrentTheme = [NSString stringWithString:@"Default"];
}
- (UIWindow*) getAppWindow
{
UIWindow* TheWindow = nil;
UIApplication* App = [UIApplication sharedApplication];
NSArray* windows = [App windows];
int i;
for(i = 0; i < [windows count]; i++)
{
TheWindow = [windows objectAtIndex:i];
if([TheWindow respondsToSelector:@selector(getCurrentTheme)])
{
break;
}
}

if(i == [windows count])
{
NSLog(@”Couldn’t find the app window, defaulting to keyWindow\n”);
TheWindow = [App keyWindow];
}

return TheWindow;
}

By request, here is a sample toggle and sample Makefile. This is the location services toggle source code.


Calling external functions and scripts from within your toggle.

As of sbsettings v3.0, you can now execute external files from your toggle. It still cannot be done directly, but instead, you will make a script that will be executed by the sbsettings root daemon (sbsettingsd). You will then send a notify_post message to the daemon to run your script. The daemon solves both of the problems of running system calls in springboard: 1) Springboard runs as mobile but most functions would require root 2) system used in springboard freezes when ram is low.

To use the daemon, you will be using notify_post commands. This function notify_post is a BSD function for system wide communication. In order to have sbsettingsd service your own post messages you need to add files to the folder /var/mobile/Library/SBSettings/Commands. The filename needs to be the post message. The file can be either a compiled executable file or a script. In your toggle, you will simply notify_post the name of your file and the daemon will receive this notification and run your file.

Here is a breakout of the steps using an example. Let’s say I want to make a toggle to respring the iPhone.

1) Create a text file file in /var/mobile/Library/SBSettings/Commands folder. In this case, lets use com.sbsettings.respring as the filename. (Note: its common practice to use the reverse dns format for notification messages). So, the first step, is to create the file /var/mobile/Library/SBSettings/Commands/com.sbsettings.respring

2) Make the file executable by setting it to 755: chmod 755 /var/mobile/Library/SBSettings/Command/com.sbsettings.respring

3) Edit this file as a script. Lets insert into com.sbsettings.respring file:

#!/bin/sh
killall SpringBoard

4) Now restart sbsettingsd. You can use launchctl or just issue killall sbsettingsd. Now the daemon is running and will listen for com.sbsettings.respring messages.

5) Time to edit your toggle code. This particular toggle will not have a separate enable and disable. So our setState() function will only do one thing. If you need to enable and disable something, you would create two files in step #1 such as com.sbsettings.enablerespring and com.sbsettings.disablerespring. Anyhow, here is the code for our setState()

void setState(BOOL Enable)
{
notify_post(“com.sbsettings.respring”);
}

That’s it!

6) Now, how do you get messages back from the daemon such as current status of your daemon? That is entirely up to you and your implementation. One easy method is to have a script such as com.sbsettings.getrespringstate that you notify_post in your isEnabled() function. This can perhaps call a command and if it returns 1, create a file in /tmp and if not delete the file in /tmp. (/tmp is good because it is cleared on a reboot automatically). Then in your isEnabled() function, you could notify_post() then usleep for a few milliseconds, then check for the file existence in /tmp. There are other options but this is the simplest.

With the new sbsettings root daemon, it is now possible to create toggles such as safari download with ease. (In fact, sbsettings 3 will most likely ship with this toggle).

Samples: source code for fast toggles, source code for brightness toggle.