.
|-- build/ # This is where our extension is built.
|-- demo/
| `-- demo.js # This is a demo Node.js script to test our extension.
|-- src/
| `-- node_gtknotify.cpp # This is the where we do the mapping from C++ to Javascript.
`-- wscript # This is our build configuration used by node-waf
这个看起来很漂亮的tree 用通用的 tree 生成.
现在让我来创建测试脚本demo.js 和决定我们扩展的API前期看起来应该像:
// This loads our extension on the notify variable.
// It will only load a constructor function, notify.notification().
var notify = require("../build/default/gtknotify.node"); // path to our extension
var notification = new notify.notification();
notification.title = "Notification title";
notification.icon = "emblem-default"; // see /usr/share/icons/gnome/16x16
notification.send("Notification message");
#include // v8 is the Javascript engine used by QNode
#include
// We will need the following libraries for our GTK+ notification
#include
#include
#include
using namespace v8;
class Gtknotify : node::ObjectWrap {
private:
public:
Gtknotify() {}
~Gtknotify() {}
static void Init(Handle
现在,我们必须把下面的代码编写到我们的Init()方法中:
声明构造函数,并将其绑定到我们的目标变量。var n = require("notification");将绑定notification() 到 n:n.notification().
// Wrap our C++ New() method so that it's accessible from Javascript
// This will be called by the new operator in Javascript, for example: new notification();
v8::Local local_function_template = v8::FunctionTemplate::New(New);
// Make it persistent and assign it to persistent_function_template which is a static attribute of our class.
Gtknotify::persistent_function_template = v8::Persistent::New(local_function_template);
// Each JavaScript object keeps a reference to the C++ object for which it is a wrapper with an internal field.
Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since a constructor function only references 1 object
// Set a "class" name for objects created with our constructor
Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));
// Set the "notification" property of our target variable and assign it to our constructor function
target->Set(String::NewSymbol("notification"), Gtknotify::persistent_function_template->GetFunction());
声明属性:n.title 和n.icon.
// Set property accessors
// SetAccessor arguments: Javascript property name, C++ method that will act as the getter, C++ method that will act as the setter
Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle);
Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("icon"), GetIcon, SetIcon);
// For instance, n.title = "foo" will now call SetTitle("foo"), n.title will now call GetTitle()
声明原型方法:n.send()
// This is a Node macro to help bind C++ methods to Javascript methods (see https://github.com/joyent/node/blob/v0.2.0/src/node.h#L34)
// Arguments: our constructor function, Javascript method name, C++ method name
NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, "send", Send);
现在我们的Init()方法看起来应该是这样的:
// Our constructor
static v8::Persistent persistent_function_template;
static void Init(Handle target) {
v8::HandleScope scope; // used by v8 for garbage collection
// Our constructor
v8::Local local_function_template = v8::FunctionTemplate::New(New);
Gtknotify::persistent_function_template = v8::Persistent::New(local_function_template);
Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since this is a constructor function
Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));
// Our getters and setters
Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle);
Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("icon"), GetIcon, SetIcon);
// Our methods
NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, "send", Send);
// Binding our constructor function to the target variable
target->Set(String::NewSymbol("notification"), Gtknotify::persistent_function_template->GetFunction());
}
New() 方法创建了我们自定义类的新实例(一个 Gtknotify 对象),并设置一些初始值,然后返回该对象的 JavaScript 处理。这是 JavaScript 使用 new 操作符调用构造函数的期望行为。
std::string title;
std::string icon;
// new notification()
static Handle New(const Arguments& args) {
HandleScope scope;
Gtknotify* gtknotify_instance = new Gtknotify();
// Set some default values
gtknotify_instance->title = "Node.js";
gtknotify_instance->icon = "terminal";
// Wrap our C++ object as a Javascript object
gtknotify_instance->Wrap(args.This());
return args.This();
}
getters 和 setters: GetTitle(), SetTitle(), GetIcon(), SetIcon()
下面主要是一些样板代码,可以归结为 C++ 和 JavaScript (v8) 之间的值转换。
// this.title
static v8::Handle GetTitle(v8::Local<:string> property, const v8::AccessorInfo& info) {
// Extract the C++ request object from the JavaScript wrapper.
Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder());
return v8::String::New(gtknotify_instance->title.c_str());
}
// this.title=
static void SetTitle(Local property, Local value, const AccessorInfo& info) {
Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder());
v8::String::Utf8Value v8str(value);
gtknotify_instance->title = *v8str;
}
// this.icon
static v8::Handle GetIcon(v8::Local<:string> property, const v8::AccessorInfo& info) {
// Extract the C++ request object from the JavaScript wrapper.
Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder());
return v8::String::New(gtknotify_instance->icon.c_str());
}
// this.icon=
static void SetIcon(Local property, Local value, const AccessorInfo& info) {
Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder());
v8::String::Utf8Value v8str(value);
gtknotify_instance->icon = *v8str;
}
原型方法: Send()
首先我们抽取 C++ 对象的 this 引用,然后使用对象的属性来构建通知并显示。
// this.send()
static v8::Handle Send(const Arguments& args) {
v8::HandleScope scope;
// Extract C++ object reference from "this"
Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(args.This());
// Convert first argument to V8 String
v8::String::Utf8Value v8str(args[0]);
// For more info on the Notify library: http://library.gnome.org/devel/libnotify/0.7/NotifyNotification.html
Notify::init("Basic");
// Arguments: title, content, icon
Notify::Notification n(gtknotify_instance->title.c_str(), *v8str, gtknotify_instance->icon.c_str()); // *v8str points to the C string it wraps
// Display the notification
n.show();
// Return value
return v8::Boolean::New(true);
}
def set_options(opt):
opt.tool_options("compiler_cxx")
def configure(conf):
conf.check_tool("compiler_cxx")
conf.check_tool("node_addon")
# This will tell the compiler to link our extension with the gtkmm and libnotifymm libraries.
conf.check_cfg(package='gtkmm-2.4', args='--cflags --libs', uselib_store='LIBGTKMM')
conf.check_cfg(package='libnotifymm-1.0', args='--cflags --libs', uselib_store='LIBNOTIFYMM')
def build(bld):
obj = bld.new_task_gen("cxx", "shlib", "node_addon")
obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall"]
# This is the name of our extension.
obj.target = "gtknotify"
obj.source = "src/node_gtknotify.cpp"
obj.uselib = ['LIBGTKMM', 'LIBNOTIFYMM']