Standard OS Libraries

This article gives the names of standard libraries that can be accessed with js-ctypes. These libraries are what enable js-ctypes to work. The alternative to standard libraries is creating your own DLL (for Windows) or SO (for Linux) file with C functions that can be called from your add-on with js-ctypes. The DLL/SO/etc file you make must be shipped with your add-on. Standard libraries offer the advantage of not having to ship anything. They are already available on the operating system for you. You just need to supply the path to appropriate files and set up the proper types of values/arguments in the js-ctypes code. This article allows you to find out what types to give to values/arguments by supplying links to the documentation of the OS libraries.

Windows

Windows has many Standard OS Libraries. The most common, found on all Windows OS version, is "WinAPI". Windows also has the "WinAPILists" and ".NET Framework" (I'm not sure if it can be accessed JSCtypes but probably). A complete list of Windows APIs can be found at the MSDN API Index.

WinAPI

This is the one that is most commonly used by js-ctypes. This comes shipped by default with all installations of Windows operating systems since Win98. It's also known as WinAPI32 or just WinAPI. A list of all the functions available through this API can be found at the MSDN WinAPI Index. For finding out the values and types of arguments and returns of the functions you want to use from this API, you must visit the functions page on this linked MSDN site; it will give you all that information.

Example: GetCursorPos

I wanted to get the mouse cursor position without using a MouseMove listener, as this is a high overhead event listener. So I went to the MSDN WinAPI Index page and then went to the "Cursors" category and came across GetCursorPos. I then searched it on MSDN and saw that it needed the user32.dll. (MSDN - GetCursorPos)

This example was created on Windows XP SP2. It is a full working example; just copy & paste it to try it out.

Components.utils.import("resource://gre/modules/ctypes.jsm");
var lib = ctypes.open("user32.dll");

/* note: if you go to GetCursorPos page on MSDN, it says first argument is of structure POINT, so lets create that structure,
 * the link here shows that that x and y are type Long which is ctypes.long
 */
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd162805%28v=vs.85%29.aspx
var POINT = new ctypes.StructType("tagPOINT", [
  { "x": ctypes.long },
  { "y": ctypes.long }
]);

/* Declare the signature of the function we are going to call */
var GetCursorPos = lib.declare('GetCursorPos',
    ctypes.winapi_abi,
    ctypes.bool,
    POINT.ptr
);

/* Use it like this */
var point = POINT();
var ret = GetCursorPos(point.address());

Components.utils.reportError(ret);
Components.utils.reportError(point);

lib.close();

Resources for WinAPI

COM

UNIX

Unix based operating systems are Linux, *BSD, Solaris, etc. There is a star/asterik ("*") before "BSD" because this means the BSD family such as FreeBSD, OpenBSD (who by the way made ssh), NetBSD, etc.

GTK+ and Qt are graphic frameworks, the graphic stack under Unix is Xorg (X11/X). GTK+ builds over X11. Xorg is being re-written at the moment, it is known as Wayland. Wayland is not considered stable, but it's supposed to replace Xorg in the long term. GTK+ and Qt have begun porting their code to Wayland. As Wayland is not out yet in the wild this article does not focus on Wayland, it will focus on GTK+ and Xorg(X11/X).

GDK is a stack under GTK+. GTK+ is the toolkit to build apps, GDK is more low-level.

To be precise, X is "just" like a display machine. On top of which everybody uses "Window Managers" (known as "WM"s) such as fluxbox, i3, awesome, openbox, Mutter (used by Gnome 3), plasma (used by KDE) etc. Gnome and KDE are "Desktop Environments" (known as "DE"s). WMs on Ubuntu work a little differently but we'll revisit this topic later in this article (Did you know they have a *special* Firefox build for Ubuntu?). So DEs are a stack over WMs.

There is also a layer known as shell, GnomeShell is the shell used in Gnome 3, it is the outter shell.

GTK+ is the framework used under Firefox (and other Mozilla applications like Thunderbird). The other framework used in Unix systems is Qt. Firefox is officially supported only for GTK+ at the moment, therefore GTK+ is required for Firefox to run. Attempting to install Firefox on a a non-GTK+ based Linux build such as KaOSx, which is Qt based (details on KaOSx at the time of this writing: KDELibs version 4.1.4.3, Qt version 4.8.6, 64bit) would install GTK+ libraries along with it in order to enable Firefox to work on the Qt system. There are rumors of a Firefox Qt that is in the works.

On Linux, libraries will be typically found as .so files. One library that Linux uses, and that can be used through js-ctypes, is X11.

GDK

As mentioned, all Firefox's are a GTK+ app, which means it has to have GDK, as GDK is underlying level of GTK+. Therefore all the GDK library functions are available for use. GDK has version 2 and 3, typically version 2 is referred to as just GDK and version 3 is referred to as GDK3. Examples for both are found below. Documentation for GDK functions are found here: GNOME Developer :: GDK 2 Reference Manual. For GDK3 documentation is found here: GNOME Developer :: GDK 3 Reference Manual. Because GNOME is in the name of the websites don't get confused and think this only works on GNOME. It is true though that GDK was developed by the GNOME team.

Example: gdk_window_get_pointer

This example is fot the GDK 2.0 library. In GDK 3.0 the gdk_window_get_device_position function was deprecated, see below for GDK 3.0 example.

Components.utils.import('resource://gre/modules/ctypes.jsm');

var gdk = ctypes.open('libgdk-x11-2.0.so.0');

// types
var gint = ctypes.int;
var GdkDevice = ctypes.StructType('GdkDevice');
var GdkModifierType = ctypes.int;
var GdkWindow = ctypes.StructType('GdkWindow');
var VOID = ctypes.void_t;

// https://developer.gnome.org/gdk3/stable/gdk3-Windows.html#gdk-get-default-root-window
var gdk_get_default_root_window = gdk.declare('gdk_get_default_root_window', ctypes.default_abi,
    GdkWindow.ptr    // return - the root window, which is top most parent of all windows
);

// in GDK2 we have to use gdk_window_get_pointer, but in GDK3 it was deprecated and have to use gdk_window_get_device_position
https://developer.gnome.org/gdk3/stable/gdk3-Windows.html#gdk-window-get-pointer
var gdk_window_get_pointer = gdk.declare('gdk_window_get_pointer', ctypes.default_abi,
    GdkWindow.ptr,        // return - the window containing the pointer (as with gdk_window_at_pointer()), or NULL if the window containing the pointer isn’t known to GDK.
    GdkWindow.ptr,        // window
    gint.ptr,            // x
    gint.ptr,            // y
    GdkModifierType.ptr    // mask
);

var winRoot_GdkWindowPtr = gdk_get_default_root_window();
console.info('winRoot_GdkWindowPtr:', winRoot_GdkWindowPtr, winRoot_GdkWindowPtr.toString());

// gdk2
var x = gint();
var y = gint();
var mask = GdkModifierType();
var win_underMouse = gdk_window_get_pointer(
    winRoot_GdkWindowPtr,
    x.address(),
    y.address(),
    mask.address()
);
console.info('win_underMouse:', win_underMouse, win_underMouse.toString());
console.info('x:', x, x.toString());
console.info('y:', y, y.toString());
console.info('mask:', mask, mask.toString());


gdk.close();

Example: gdk_window_get_device_position

This example works only for GDK3, as the function gdk_window_get_device_position was not available in GDK2, for GDK2 equivalent see above

WARNING THIS EXAMPLE DOES NOT WORK YET IT NEEDS SOME BUG FIXING

Components.utils.import('resource://gre/modules/ctypes.jsm');
var gdk = ctypes.open('libgdk-x11-2.0.so.0');
var gdk3 = ctypes.open('libgdk-3.so.0');

// types
var gint = ctypes.int;
var GdkDevice = ctypes.StructType('GdkDevice');
var GdkDeviceManager = ctypes.StructType('GdkDeviceManager');
var GdkDisplay = ctypes.StructType('GdkDisplay');
var GdkModifierType = ctypes.int;
var GdkWindow = ctypes.StructType('GdkWindow');

// https://developer.gnome.org/gdk3/stable/gdk3-Windows.html#gdk-get-default-root-window
var gdk_get_default_root_window = gdk.declare('gdk_get_default_root_window', ctypes.default_abi,
    GdkWindow.ptr    // return - the root window, which is top most parent of all windows
);

// in GDK2 we have to use gdk_window_get_pointer, but in GDK3 it was deprecated and have to use gdk_window_get_device_position
// https://developer.gnome.org/gdk3/stable/gdk3-Windows.html#gdk-window-get-device-position
var gdk_window_get_device_position = gdk3.declare('gdk_window_get_device_position', ctypes.default_abi,
    GdkWindow.ptr,        // return - The window underneath device or NULL if the window is not known to GDK
    GdkWindow.ptr,        // window
    GdkDevice.ptr,        // device
    gint.ptr,            // x
    gint.ptr,            // y
    GdkModifierType.ptr    // mask
);

// https://developer.gnome.org/gdk3/stable/gdk3-Windows.html#gdk-window-get-display
var gdk_window_get_display = gdk.declare('gdk_window_get_display', ctypes.default_abi,
    GdkDisplay.ptr,    // return
    GdkWindow.ptr    // *window
);

// https://developer.gnome.org/gdk3/stable/GdkDisplay.html#gdk-display-get-device-manager
var gdk_display_get_device_manager = gdk3.declare('gdk_display_get_device_manager', ctypes.default_abi,
    GdkDeviceManager.ptr,    // return
    GdkDisplay.ptr    // *display
);

// https://developer.gnome.org/gdk3/stable/GdkDeviceManager.html#gdk-device-manager-get-client-pointer
var gdk_device_manager_get_client_pointer = gdk3.declare('gdk_device_manager_get_client_pointer', ctypes.default_abi,
    GdkDevice.ptr,    // return
    GdkDeviceManager.ptr    // *device_manager
);

var winRoot_GdkWindowPtr = gdk_get_default_root_window();
var displayPtr = gdk_window_get_display(winRoot_GdkWindowPtr);
var device_managerPtr = gdk_display_get_device_manager(displayPtr);
var devicePtr = gdk_device_manager_get_client_pointer(device_managerPtr);

var x = gint();
var y = gint();
var mask = GdkModifierType();
var win_underMouse = gdk_window_get_device_position(
    winRoot_GdkWindowPtr,
    devicePtr,
    x.address(),
    y.address(),
    mask.address()
);
console.info('win_underMouse:', win_underMouse, win_underMouse.toString());
console.info('x:', x, x.toString());
console.info('y:', y, y.toString());
console.info('mask:', mask, mask.toString());

gdk.close();
gdk3.close();

GTK+

The GTK+ toolkit can also be used. The list of the exposed symbols/functions can be found here: GNOME Developer :: GTK+ Reference Manual.

Example: ...

// placeholder - example soon to come

X11

X11 runs primarily on UNIX® and UNIX-like operating systems like Linux, all of the BSD variants, Sun Solaris both native 32 and 64 bit support, Solaris x86, Mac OS X (via Darwin) as well as other platforms like OS/2 and Cygwin. There are updates that happen to X11; at the time of this writing, the latest version is 4.4.0. Information on all releases can be found on its official page X11 Resources. Syntax and variable types are found on the documentation pages linked from this page. For example the documentation for v4.4.0 is seen here: X11 Manual pages: Section 3.

Example: XQueryPointer

Get mouse cursor position and some extra information. Documentation on this function is found at XFree86 :: XQueryPointer(3X11) Manual Page.

Components.utils.import("resource://gre/modules/ctypes.jsm");
let X11 = ctypes.open("libX11.so.6");
let Display_ptr = ctypes.voidptr_t;
let XOpenDisplay = X11.declare("XOpenDisplay",
    ctypes.default_abi,
    Display_ptr,
    ctypes.char.ptr
);
let Window = ctypes.int;
let XRootWindow = X11.declare("XRootWindow",
    ctypes.default_abi,
    Window,
    Display_ptr,
    ctypes.int
);
let XQueryPointer = X11.declare("XQueryPointer",
    ctypes.default_abi,
    ctypes.bool,
    Display_ptr,
    Window,
    Window.ptr,
    Window.ptr,
    ctypes.int.ptr,
    ctypes.int.ptr,
    ctypes.int.ptr,
    ctypes.int.ptr,
    ctypes.unsigned_int.ptr
);
let XCloseDisplay = X11.declare("XCloseDisplay",
    ctypes.default_abi,
    ctypes.int,
    Display_ptr
);

let display = XOpenDisplay(null);
let rootWindow = XRootWindow(display, 0);

let root = new Window();
let child = new Window();
let rootX = new ctypes.int();
let rootY = new ctypes.int();
let windowX = new ctypes.int();
let windowY = new ctypes.int();
let mask = new ctypes.unsigned_int();

XQueryPointer(display,
    rootWindow,
    root.address(),
    child.address(),
    rootX.address(),
    rootY.address(),
    windowX.address(),
    windowY.address(),
    mask.address()
);

XCloseDisplay(display);

Components.utils.reportError(rootX.value + "," + rootY.value);

X11.close();

Resources for X11

XCB

All the above methods, GDK, GTK, and X11/Xlib are meant to be used on the main thread. If you would like to do stuff off of the main thread, in ChromeWorker's, then you should use XCB. XCB is thread safe.

Resources for XCB

Mac OS X

Mac OS X has two categories of C-based API (Carbon, Core Foundation) and Objective-C based API (Cocoa).

Note: You cannot use Carbon routines from your add-on; Firefox is a 64-bit application, and you cannot use Carbon from 64-bit code.

Core Foundation

To learn about all the Mac OS X APIs and which library file you will need to call, go to the Mac Developer Library website and find the function, then scroll down to "Declared In" section, and find which Framework contains the header file.

The CoreGraphics framework is based on CoreFoundation framework, including CoreGraphics etc. The release, string, etc. comes from CoreFoundation.

Example: CGEventGetLocation

For example, CGEventGetLocation function is declared in CGEvent.h header file, which is contained in CoreGraphics.framework. So /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics is the library path to pass to ctypes.open function call.

Components.utils.import("resource://gre/modules/ctypes.jsm");
let CoreGraphics = ctypes.open("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics");
let CoreFoundation = ctypes.open("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");

let CGEventRef = ctypes.voidptr_t;
let CGEventSourceRef = ctypes.voidptr_t;
let CGEventCreate = CoreGraphics.declare("CGEventCreate",
                                         ctypes.default_abi,
                                         CGEventRef,
                                         CGEventSourceRef);
let CGFloat = ctypes.voidptr_t.size == 4 ? ctypes.float : ctypes.double;  // 64bit its double, in 32bit its float
let CGPoint = new ctypes.StructType("CGPoint",
                                     [ { "x" : CGFloat },
                                       { "y" : CGFloat } ]);
let CGEventGetLocation = CoreGraphics.declare("CGEventGetLocation",
                                              ctypes.default_abi,
                                              CGPoint,
                                              CGEventRef);
let CFTypeRef = ctypes.voidptr_t;
let CFRelease = CoreFoundation.declare("CFRelease",
                                       ctypes.default_abi,
                                       ctypes.void_t,
                                       CGEventRef);

let event = CGEventCreate(null);
let cursor = CGEventGetLocation(event);

CFRelease(event);

Components.utils.reportError(cursor);

CoreGraphics.close();
CoreFoundation.close();

Resources for Core Foundation

Cocoa

To call Objective-C based API from ctypes, use the following functions in libobjc.dylib:

  • objc_getClass to get Class
  • sel_registerName to register selector name
  • objc_msgSend and some variants to send message to class and instance

Objective-C code can be translated into C code by the following rule:

// Objective-C code
NSEvent loc = [NSEvent mouseLocation];

// pseudo C code
NSEvent loc = (NSPoint)objc_msgSend(objc_getClass("NSEvent"),
                                    sel_registerName("mouseLocation"));

Example: [NSEvent mouseLocation]

Components.utils.import("resource://gre/modules/ctypes.jsm");
let objc = ctypes.open(ctypes.libraryName("objc"));

let id = ctypes.StructType("objc_object").ptr;
let SEL = ctypes.StructType("objc_selector").ptr;
let objc_getClass = objc.declare("objc_getClass",
                                 ctypes.default_abi,
                                 id,
                                 ctypes.char.ptr);
let sel_registerName = objc.declare("sel_registerName",
                                    ctypes.default_abi,
                                    SEL,
                                    ctypes.char.ptr);
let objc_msgSend = objc.declare("objc_msgSend",
                                ctypes.default_abi,
                                id,
                                id,
                                SEL,
                                "...");
let CGFloat = ctypes.voidptr_t.size == 4 ? ctypes.float : ctypes.double;  // 64bit its double, in 32bit its float
let NSPoint = new ctypes.StructType("NSPoint",
                                    [ { "x" : CGFloat },
                                      { "y" : CGFloat } ]);
// note: [NSEvent mouseLocation] returns NSPoint struct,
// which is small enough to return in register,
// so we don't need to use objc_msgSend_stret.
let objc_msgSend_NSPoint = objc.declare("objc_msgSend",
                                        ctypes.default_abi,
                                        NSPoint,
                                        id,
                                        SEL,
                                        "...");

// loc = [NSEvent mouseLocation]
let NSEvent = objc_getClass("NSEvent");
let mouseLocation = sel_registerName("mouseLocation");
let loc = objc_msgSend_NSPoint(NSEvent, mouseLocation);

Components.utils.reportError(loc);

objc.close();

Resources for Cocoa

Android

Android runs on Java and can be used by js-ctypes through the JNI libraries. Thankfully all the js-ctypes was abstracted away into JNI.jsm. Undrestanding js-ctypes is important as arrays and other features used in JNI.jsm will require us to create arrays from the types available to the ctypes.jsm module.

Although JNI is done completely through js-ctypes, a JSM was created to abstract away the js-ctypes so developers can focus on the JNI aspect. Therefore elaboration on this technique is not done in the js-ctypes section but it is done in the JNI.jsm article. This article links to a fully funcitonal demo that can be copied and pasted into scratchpad, as a quick reference here is the link to that demo: Following the Android Toasts Tutorial from a JNI Prespective.

See Also