├── COPYING ├── README.md ├── binding.gyp ├── client-protocol.js ├── client.js ├── examples ├── keyboard.coffee ├── list_interfaces.coffee ├── mouse.coffee ├── simple-shm.coffee └── util.coffee ├── screenshots └── 2013-4-13-simple-shm.jpg ├── src ├── array.cc ├── array.h ├── client.cc ├── interface.cc ├── interface.h ├── proxy.cc └── proxy.h └── tools └── nodeland-scanner.py /COPYING: -------------------------------------------------------------------------------- 1 | Copyright © 2013 Henri Tuhola and other contributors (if they arise) 2 | 3 | Permission to use, copy, modify, distribute, and sell this software and its 4 | documentation for any purpose is hereby granted without fee, provided that 5 | the above copyright notice appear in all copies and that both that copyright 6 | notice and this permission notice appear in supporting documentation, and 7 | that the name of the copyright holders not be used in advertising or 8 | publicity pertaining to distribution of the software without specific, 9 | written prior permission. The copyright holders make no representations 10 | about the suitability of this software for any purpose. It is provided "as 11 | is" without express or implied warranty. 12 | 13 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 14 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 15 | EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 16 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 17 | DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 19 | OF THIS SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | These are work-in-progress node.js bindings. I'll supply examples and screenshots when it can be used. 2 | 3 | It should work with wayland 1.4.0 and newer. 4 | 5 | Task List: 6 | 7 | * Verify that the Proxy::Marshal doesn't leak memory and works properly in every situation. 8 | * Verify that interface signature handling code is correct. 9 | * Write some more examples, see whether the new bindings work properly. 10 | * Verify that everything works. 11 | * Find a good method for listener to provide the correct proxy. 12 | * Provide interface parser, for users to access weston interfaces from their apps. 13 | * Write example for drag&drop and copy&paste. 14 | * Find some code that crashes, then put the system go into known state instead of crashing. 15 | * Provide wayland-egl bindings and tune cheery's webgl-bindings to work with wayland. 16 | * Write a small toolkit that provides every feature provided by wayland. 17 | * Find nice lib which loads PNG or JPG files in RGBA -format. 18 | * Write a tiny blitting engine. 19 | * Fill the task list with more tasks when you know what to do. 20 | 21 | 22 | Bitcoin address for donations: 19Cjd98dNM4SRoNP1t1qbYunYCz8TgjQAm (node-wayland) 23 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "wayland_client", 5 | "sources": [ 6 | "src/array.cc", 7 | "src/interface.cc", 8 | "src/proxy.cc", 9 | "src/client.cc", 10 | ], 11 | "libraries": [ 12 | " @formats |= 1 << format 18 | seat.capabilities = 0 19 | seat.listen capabilities: (flags) -> @capabilities = flags 20 | display.roundtrip() 21 | unless shm.formats & (1 << wl.FORMAT_XRGB8888) 22 | throw "SHM_FORMAT_XRGB8888 not available" 23 | unless seat.capabilities & seat.CAPABILITY_POINTER 24 | throw "seat has no mouse" 25 | unless seat.capabilities & seat.CAPABILITY_KEYBOARD 26 | throw "seat has no keyboard" 27 | 28 | cursor = util.create_buffer(shm, 32, 32) 29 | cursor.surface = compositor.create_surface() 30 | update_cursor = () -> 31 | for i in [0...cursor.width*cursor.height] 32 | x = (i % cursor.width) 33 | y = Math.floor(i / cursor.width) 34 | x_even = Math.floor(x/4) % 2 == 0 35 | y_even = Math.floor(y/4) % 2 == 0 36 | value = 0xff 37 | if x_even ^ y_even 38 | value = 0x40 39 | cursor.data[i*4+3] = value 40 | cursor.data[i*4+2] = value 41 | cursor.data[i*4+1] = value 42 | cursor.data[i*4+0] = value 43 | 44 | cursor.surface.attach(cursor.buffer, 0, 0) 45 | cursor.surface.damage(0, 0, cursor.width, cursor.height) 46 | cursor.surface.commit() 47 | cursor.busy = true 48 | 49 | 50 | window = util.create_widget(compositor, shell, shm, 256, 256) 51 | window.shellface.set_toplevel() 52 | window.fullscreen = false 53 | 54 | redraw = (time) -> 55 | {width, height, data, busy} = window.buffers.back 56 | if busy then throw "buffers busy, can't draw" 57 | 58 | offset = Math.sin(time) 59 | 60 | for i in [0...width*height] 61 | x = (i % width) 62 | y = Math.floor(i / width) 63 | value = 0x80 + offset * 0x80 64 | x_even = Math.floor(x/16) % 2 == 0 65 | y_even = Math.floor(y/16) % 2 == 0 66 | if x_even ^ y_even 67 | value = 0x40 68 | data[i*4+3] = value 69 | data[i*4+2] = value 70 | data[i*4+1] = value 71 | data[i*4+0] = value 72 | 73 | window.frame(redraw) 74 | window.flip() 75 | 76 | redraw(0) 77 | 78 | keyboard = seat.get_keyboard() 79 | keyboard.listen { 80 | enter: (serial, surface, keys) -> 81 | console.log "keyboard enter", serial, surface, keys 82 | leave: (serial, surface) -> 83 | console.log "keyboard leave", serial, surface 84 | key: (serial, time, key, state) -> 85 | console.log "key", serial, time, key, state 86 | if key == 87 and state == 1 87 | if window.fullscreen 88 | window.fullscreen = false 89 | window.shellface.set_toplevel() 90 | else 91 | window.fullscreen = true 92 | window.shellface.set_fullscreen( 93 | window.shellface.FULLSCREEN_METHOD_SCALE, 94 | 30, 95 | null 96 | ) 97 | } 98 | 99 | pointer = seat.get_pointer() 100 | pointer.inside = null 101 | pointer.listen { 102 | motion: (time, x, y) -> 103 | # console.log "motion #{time} (#{x},#{y})" 104 | button: (serial, time, button, state) -> 105 | console.log "button #{serial} #{time} #{button} #{state}" 106 | console.log 'this inside', this.inside 107 | console.log 'window surface', window.surface 108 | if state == 1 and this.inside == window.surface 109 | window.shellface.move(seat, serial) 110 | axis: (time, axis, value) -> 111 | # console.log "button #{time} #{axis} #{value}" 112 | enter: (serial, which, x, y) -> 113 | console.log "enter #{serial} #{which} (#{x},#{y})" 114 | this.set_cursor(serial, cursor.surface, 32, 32) 115 | update_cursor() 116 | this.inside = which 117 | leave: (serial, which) -> 118 | # console.log "leave #{serial} #{which}" 119 | this.inside = null 120 | } 121 | 122 | # create_data_source 123 | data_device = data_device_manager.get_data_device(seat) 124 | data_device.proxy.listen (args...) -> 125 | console.log args 126 | 127 | ret = 0 128 | while ret != -1 129 | ret = display.dispatch() 130 | console.log ret 131 | -------------------------------------------------------------------------------- /examples/list_interfaces.coffee: -------------------------------------------------------------------------------- 1 | wl = require('../client') 2 | 3 | display = wl.connect() 4 | 5 | registry = display.get_registry() 6 | registry.listen { 7 | global: (id, name, version) -> 8 | console.log 'global', id, name, version 9 | } 10 | 11 | display.roundtrip() 12 | display.disconnect() 13 | -------------------------------------------------------------------------------- /examples/mouse.coffee: -------------------------------------------------------------------------------- 1 | wl = require '../client' 2 | fs = require 'fs' 3 | 4 | display = wl.connect() 5 | 6 | use = { 7 | wl_compositor: ['compositor', 1], 8 | wl_shell: ['shell', 1], 9 | wl_shm: ['shm', 1], 10 | wl_seat: ['seat', 1], 11 | } 12 | registry = display.get_registry() 13 | registry.listen { 14 | global: (id, name, version) -> 15 | target = use[name] 16 | if target 17 | [key, version] = target 18 | display[key] = registry.bind(id, wl.interfaces[name], version) 19 | } 20 | display.roundtrip() 21 | 22 | display.shm.formats = 0 23 | display.shm.listen { 24 | format: (format) -> 25 | display.shm.formats |= 1 << format 26 | } 27 | 28 | display.seat.capabilities = 0 29 | display.seat.listen { 30 | capabilities: (flags) -> this.capabilities = flags 31 | } 32 | display.roundtrip() 33 | unless display.shm.formats & (1 << wl.FORMAT_XRGB8888) 34 | throw "SHM_FORMAT_XRGB8888 not available" 35 | unless display.seat.capabilities & display.seat.CAPABILITY_POINTER 36 | throw "seat has no mouse" 37 | 38 | window = {} 39 | window.surface = display.compositor.create_surface() 40 | window.shell_surface = display.shell.get_shell_surface(window.surface) 41 | window.shell_surface.listen { 42 | ping: (serial) -> window.shell_surface.pong(serial) 43 | } 44 | window.shell_surface.set_title("simple-shm") 45 | window.shell_surface.set_toplevel() 46 | 47 | info = {} 48 | info.width = 256 49 | info.height = 256 50 | info.format = display.shm.FORMAT_XRGB8888 51 | info.stride = info.width*4 52 | info.size = info.stride*info.height 53 | info.fd = wl.create_anonymous_file() 54 | fs.truncateSync(info.fd, info.size) 55 | data = wl.mmap_fd(info.fd, info.size) 56 | 57 | for i in [0...info.width*info.height] 58 | x = i % info.width 59 | y = Math.floor(i / info.width) 60 | value = 0xA0 61 | x_even = Math.floor(x/16) % 2 == 0 62 | y_even = Math.floor(y/16) % 2 == 0 63 | if x_even ^ y_even 64 | value = 0x40 65 | data[i*4+3] = value 66 | data[i*4+2] = value 67 | data[i*4+1] = value 68 | data[i*4+0] = value 69 | 70 | pool = display.shm.create_pool(info.fd, info.size) 71 | buffer = pool.create_buffer(0, info.width, info.height, info.stride, info.format) 72 | info.busy = false 73 | buffer.listen { 74 | release: () -> info.busy = false 75 | } 76 | pool.destroy() 77 | fs.close(info.fd) 78 | 79 | window.surface.attach buffer, 0, 0 80 | window.surface.damage 0, 0, info.width, info.height 81 | window.surface.commit() 82 | info.busy = true 83 | 84 | display.roundtrip() 85 | 86 | console.log "window surface id #{window.surface.proxy.get_id()}" 87 | 88 | # the enter and leave events return an ID to proxy, though 89 | # I'm not sure which way to retrieve it yet. 90 | # so I just drop out an id. 91 | 92 | # interface requests a cursor image at enter. I give it nothing 93 | # so the cursor disappears when it goes into my window. 94 | 95 | # the seat seems to not allow me to grab it for the program. 96 | 97 | pointer = display.seat.get_pointer() 98 | pointer.listen { 99 | motion: (time, x, y) -> 100 | console.log "motion #{time} (#{x},#{y})" 101 | button: (serial, time, button, state) -> 102 | console.log "button #{serial} #{time} #{button} #{state}" 103 | if state == 1 and this.inside == window.surface.proxy.get_id() 104 | window.shell_surface.move(display.seat, serial) 105 | axis: (time, axis, value) -> 106 | console.log "button #{time} #{axis} #{value}" 107 | enter: (serial, which, x, y) -> 108 | console.log "enter #{serial} #{which} (#{x},#{y})" 109 | this.set_cursor(serial, null, 0, 0) 110 | this.inside = which 111 | leave: (serial, which) -> 112 | console.log "leave #{serial} #{which}" 113 | this.inside = null 114 | } 115 | 116 | ret = 0 117 | while ret != -1 118 | ret = display.dispatch() 119 | console.log ret 120 | -------------------------------------------------------------------------------- /examples/simple-shm.coffee: -------------------------------------------------------------------------------- 1 | wl = require '../client' 2 | fs = require 'fs' 3 | 4 | display = wl.connect() 5 | use = { 6 | wl_compositor: ['compositor', 1], 7 | wl_shell: ['shell', 1], 8 | wl_shm: ['shm', 1], 9 | } 10 | registry = display.get_registry() 11 | registry.listen { 12 | global: (id, name, version) -> 13 | target = use[name] 14 | if target 15 | [key, version] = target 16 | display[key] = registry.bind(id, wl.interfaces[name], version) 17 | } 18 | display.roundtrip() 19 | 20 | display.shm.formats = 0 21 | display.shm.listen { 22 | format: (format) -> 23 | display.shm.formats |= 1 << format 24 | } 25 | display.roundtrip() 26 | unless display.shm.formats & (1 << wl.FORMAT_XRGB8888) 27 | throw "SHM_FORMAT_XRGB8888 not available" 28 | 29 | window = {} 30 | window.surface = display.compositor.create_surface() 31 | window.shell_surface = display.shell.get_shell_surface(window.surface) 32 | window.shell_surface.listen { 33 | ping: (serial) -> window.shell_surface.pong(serial) 34 | } 35 | window.shell_surface.set_title("simple-shm") 36 | window.shell_surface.set_toplevel() 37 | #if you uncomment this line, remove the set_toplevel -line. 38 | #window.shell_surface.set_fullscreen(window.shell_surface.FULLSCREEN_METHOD_SCALE, 30, null) 39 | 40 | info = {} 41 | info.width = 256 42 | info.height = 256 43 | info.format = display.shm.FORMAT_XRGB8888 44 | info.stride = info.width*4 45 | info.size = info.stride*info.height 46 | info.fd = wl.create_anonymous_file() 47 | fs.truncateSync(info.fd, info.size) 48 | data = wl.mmap_fd(info.fd, info.size) 49 | 50 | for i in [0...info.width*info.height] 51 | x = i % info.width 52 | y = Math.floor(i / info.width) 53 | value = 0xA0 54 | x_even = Math.floor(x/16) % 2 == 0 55 | y_even = Math.floor(y/16) % 2 == 0 56 | if x_even ^ y_even 57 | value = 0x40 58 | data[i*4+3] = value 59 | data[i*4+2] = value 60 | data[i*4+1] = value 61 | data[i*4+0] = value 62 | 63 | pool = display.shm.create_pool(info.fd, info.size) 64 | buffer = pool.create_buffer(0, info.width, info.height, info.stride, info.format) 65 | info.busy = false 66 | buffer.listen { 67 | release: () -> info.busy = false 68 | } 69 | pool.destroy() 70 | fs.close(info.fd) 71 | 72 | window.surface.attach buffer, 0, 0 73 | window.surface.damage 0, 0, info.width, info.height 74 | window.surface.commit() 75 | info.busy = true 76 | 77 | ret = 0 78 | while ret != -1 79 | ret = display.dispatch() 80 | console.log ret 81 | -------------------------------------------------------------------------------- /examples/util.coffee: -------------------------------------------------------------------------------- 1 | wl = require '../client' 2 | fs = require 'fs' 3 | 4 | exports.get_globals = (registry) -> 5 | out = {} 6 | registry.listen global: (id, name, version) -> 7 | out[name] ?= [] 8 | out[name].push {id, version} 9 | 10 | return { 11 | bind_one: (name, version) -> 12 | unless out[name] and out[name].length > 0 13 | return null 14 | {id, newest} = out[name].pop() 15 | version ?= newest 16 | return registry.bind(id, wl.interfaces[name], version) 17 | } 18 | 19 | exports.create_buffer = (shm, width, height) -> 20 | format = shm.FORMAT_XRGB8888 21 | stride = width*4 22 | size = stride*height 23 | fd = wl.create_anonymous_file() 24 | fs.truncateSync(fd, size) 25 | data = wl.mmap_fd(fd, size) 26 | pool = shm.create_pool(fd, size) 27 | buffer = pool.create_buffer(0, width, height, stride, format) 28 | obj = {width, height, format, stride, size, data, buffer, busy:false} 29 | buffer.listen release: () -> obj.busy = false 30 | pool.destroy() 31 | fs.close(fd) 32 | return obj 33 | 34 | exports.create_surface 35 | 36 | exports.create_widget = (compositor, shell, shm, width, height) -> 37 | surface = compositor.create_surface() 38 | shellface = shell.get_shell_surface(surface) 39 | shellface.listen 40 | ping: (serial) -> shellface.pong(serial) 41 | 42 | front = exports.create_buffer(shm, width, height) 43 | back = exports.create_buffer(shm, width, height) 44 | 45 | return { 46 | surface, 47 | shellface, 48 | width, 49 | height, 50 | buffers: {front, back} 51 | flip: () -> 52 | {front, back} = @buffers 53 | surface.attach(back.buffer, 0, 0) 54 | surface.damage(0, 0, @width, @height) 55 | surface.commit() 56 | back.busy = true 57 | @buffers = front:back, back:front 58 | frame: (fn) -> 59 | if @callback then throw "frame already requested" 60 | @callback = @surface.frame() 61 | @callback.listen done: (time) => 62 | @callback.destroy() 63 | @callback = undefined 64 | fn.call(@, time / 1000) 65 | } 66 | -------------------------------------------------------------------------------- /screenshots/2013-4-13-simple-shm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheery/node-wayland/cc433c31bf2fd33a589e124581db736343ff7668/screenshots/2013-4-13-simple-shm.jpg -------------------------------------------------------------------------------- /src/array.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace v8; 6 | 7 | extern "C" { 8 | #include 9 | #include 10 | #include 11 | } 12 | 13 | #include "array.h" 14 | 15 | static Persistent arraybuffer_constructor; 16 | 17 | namespace ArrayBuffer { 18 | void DemandInit() { 19 | if (arraybuffer_constructor.IsEmpty()) { 20 | Local global = Context::GetCurrent()->Global(); 21 | Local val = global->Get(String::New("ArrayBuffer")); 22 | assert(!val.IsEmpty() && "type not found: ArrayBuffer"); 23 | assert(val->IsFunction() && "not a constructor: ArrayBuffer"); 24 | arraybuffer_constructor = Persistent::New(val.As()); 25 | } 26 | }; 27 | Local New(size_t size, void* data) { 28 | DemandInit(); 29 | Local arg_size = Integer::NewFromUnsigned(size); 30 | Local array = arraybuffer_constructor->NewInstance(1, &arg_size); 31 | if (data != NULL) SetData(array, size, data); 32 | return array; 33 | } 34 | Local Wrap(size_t size, void* data) { 35 | DemandInit(); 36 | Local arg_size = Integer::NewFromUnsigned(size); 37 | Local array = arraybuffer_constructor->NewInstance(1, &arg_size); 38 | array->SetIndexedPropertiesToExternalArrayData(data, kExternalByteArray, size); 39 | return array; 40 | } 41 | 42 | void SetData(Local array, size_t size, void* data) { 43 | void* dst = array->GetIndexedPropertiesExternalArrayData(); 44 | memcpy(dst, data, size); 45 | } 46 | 47 | void* GetData(Local array, size_t* size) { 48 | *size = array->Get(String::NewSymbol("byteLength"))->IntegerValue(); 49 | return array->GetIndexedPropertiesExternalArrayData(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/array.h: -------------------------------------------------------------------------------- 1 | namespace ArrayBuffer { 2 | Local New(size_t size, void* data); 3 | Local Wrap(size_t size, void* data); 4 | void SetData(Local array, size_t size, void* data); 5 | void* GetData(Local array, size_t* size); 6 | } 7 | -------------------------------------------------------------------------------- /src/client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace v8; 6 | 7 | extern "C" { 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | } 14 | 15 | #include "array.h" 16 | #include "interface.h" 17 | #include "proxy.h" 18 | 19 | //class Display : public node::ObjectWrap { 20 | // struct wl_display* display; 21 | // static inline Display* AsDisplay(Handle object) { 22 | // Display* display = ObjectWrap::Unwrap(object); 23 | // if (display->display == NULL) return NULL; 24 | // return display; 25 | // }; 26 | //public: 27 | // static void Init(Handle target) { 28 | // Local tpl = FunctionTemplate::New(New); 29 | // tpl->SetClassName(String::NewSymbol("Display")); 30 | // tpl->InstanceTemplate()->SetInternalFieldCount(1); 31 | // NODE_SET_PROTOTYPE_METHOD(tpl, "close", Close); 32 | // NODE_SET_PROTOTYPE_METHOD(tpl, "fileno", Fileno); 33 | // NODE_SET_PROTOTYPE_METHOD(tpl, "flush", Flush); 34 | // NODE_SET_PROTOTYPE_METHOD(tpl, "roundtrip", Roundtrip); 35 | // //tpl->PrototypeTemplate()->Set( String::NewSymbol("close"), FunctionTemplate::New(Close)->GetFunction()); 36 | // Persistent constructor = Persistent::New(tpl->GetFunction()); 37 | // target->Set(String::NewSymbol("Display"), constructor); 38 | // }; 39 | // static Handle New(const Arguments& args) { 40 | // HandleScope scope; 41 | // Display* display = new Display(); 42 | // display->display = wl_display_connect(NULL); 43 | // if (display->display == NULL) return ThrowException(String::New("failed to connect")); 44 | // display->Wrap(args.This()); 45 | // return args.This(); 46 | // }; 47 | // 48 | //}; 49 | static Handle Connect(const Arguments& args) { 50 | HandleScope scope; 51 | const char* name; 52 | if (args[0]->IsUndefined() || args[0]->IsNull()) { 53 | name = NULL; 54 | } else { 55 | v8::String::AsciiValue string(args[0]); 56 | name = *string; 57 | } 58 | wl_display* display = wl_display_connect(name); 59 | if (display == NULL) return ThrowException(String::New("failed to connect")); 60 | const unsigned argc = 2; 61 | Handle argv[argc] = { 62 | External::Wrap(display), 63 | External::Wrap((void*)&wl_display_interface), 64 | }; 65 | Local instance = Proxy::constructor->NewInstance(argc, argv); 66 | return scope.Close(instance); 67 | } 68 | 69 | static Handle ConnectToFd(const Arguments& args) { 70 | HandleScope scope; 71 | wl_display* display = wl_display_connect_to_fd(args[0]->IntegerValue()); 72 | if (display == NULL) return ThrowException(String::New("failed to connect")); 73 | const unsigned argc = 2; 74 | Handle argv[argc] = { 75 | External::Wrap(display), 76 | External::Wrap((void*)&wl_display_interface), 77 | }; 78 | Local instance = Proxy::constructor->NewInstance(argc, argv); 79 | return scope.Close(instance); 80 | } 81 | 82 | static Handle DisplayDisconnect(const Arguments& args) { 83 | HandleScope scope; 84 | Proxy* display = Proxy::AsProxy(args[0]->ToObject()); 85 | if (display == NULL) return ThrowException(String::New("not connected")); 86 | wl_display_disconnect((wl_display*)display->proxy); 87 | display->Free(); 88 | return scope.Close(Undefined()); 89 | }; 90 | static Handle DisplayFileno(const Arguments& args) { 91 | HandleScope scope; 92 | Proxy* display = Proxy::AsProxy(args[0]->ToObject()); 93 | if (display == NULL) return ThrowException(String::New("not connected")); 94 | return scope.Close(Integer::New(wl_display_get_fd((wl_display*)display->proxy))); 95 | }; 96 | 97 | static Handle DisplayFlush(const Arguments& args) { 98 | HandleScope scope; 99 | Proxy* display = Proxy::AsProxy(args[0]->ToObject()); 100 | if (display == NULL) return ThrowException(String::New("not connected")); 101 | wl_display_flush((wl_display*)display->proxy); 102 | return scope.Close(Undefined()); 103 | }; 104 | 105 | static Handle DisplayRoundtrip(const Arguments& args) { 106 | HandleScope scope; 107 | Proxy* display = Proxy::AsProxy(args[0]->ToObject()); 108 | if (display == NULL) return ThrowException(String::New("not connected")); 109 | wl_display_roundtrip((wl_display*)display->proxy); 110 | return scope.Close(Undefined()); 111 | }; 112 | 113 | static Handle DisplayDispatch(const Arguments& args) { 114 | HandleScope scope; 115 | Proxy* display = Proxy::AsProxy(args[0]->ToObject()); 116 | if (display == NULL) return ThrowException(String::New("not connected")); 117 | return scope.Close(Integer::New(wl_display_dispatch((wl_display*)display->proxy))); 118 | }; 119 | 120 | static Handle MemoryMapFile(const Arguments& args) { 121 | HandleScope scope; 122 | size_t size = args[1]->IntegerValue(); 123 | void* data = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, args[0]->IntegerValue(), 0); 124 | return scope.Close(ArrayBuffer::Wrap(size, data)); 125 | }; 126 | 127 | static Handle MemoryUnmapFile(const Arguments& args) { 128 | HandleScope scope; 129 | size_t size; 130 | void* data = ArrayBuffer::GetData(args[0]->ToObject(), &size); 131 | munmap(data, size); 132 | return scope.Close(Undefined()); 133 | }; 134 | 135 | static 136 | int create_anon_file() { 137 | char name[] = "/tmp/wayland-shared-XXXXXX"; 138 | int fd = mkostemp(name, O_CLOEXEC); 139 | if (fd >= 0) unlink(name); 140 | return fd; 141 | } 142 | 143 | static Handle CreateAnonFile(const Arguments& args) { 144 | HandleScope scope; 145 | return scope.Close(Integer::New(create_anon_file())); 146 | }; 147 | 148 | static void Init(Handle target) { 149 | Interface::Init(target); 150 | Proxy::Init(target); 151 | target->Set(String::NewSymbol("connect"), 152 | FunctionTemplate::New(Connect)->GetFunction()); 153 | target->Set(String::NewSymbol("connect_to_fd"), 154 | FunctionTemplate::New(ConnectToFd)->GetFunction()); 155 | target->Set(String::NewSymbol("get_interface_by_name"), 156 | FunctionTemplate::New(Interface::GetInterfaceByName)->GetFunction()); 157 | target->Set(String::NewSymbol("display_disconnect"), 158 | FunctionTemplate::New(DisplayDisconnect)->GetFunction()); 159 | target->Set(String::NewSymbol("display_fileno"), 160 | FunctionTemplate::New(DisplayFileno)->GetFunction()); 161 | target->Set(String::NewSymbol("display_flush"), 162 | FunctionTemplate::New(DisplayFlush)->GetFunction()); 163 | target->Set(String::NewSymbol("display_roundtrip"), 164 | FunctionTemplate::New(DisplayRoundtrip)->GetFunction()); 165 | target->Set(String::NewSymbol("display_dispatch"), 166 | FunctionTemplate::New(DisplayDispatch)->GetFunction()); 167 | target->Set(String::NewSymbol("mmap_fd"), 168 | FunctionTemplate::New(MemoryMapFile)->GetFunction()); 169 | target->Set(String::NewSymbol("munmap_fd"), 170 | FunctionTemplate::New(MemoryUnmapFile)->GetFunction()); 171 | target->Set(String::NewSymbol("create_anonymous_file"), 172 | FunctionTemplate::New(CreateAnonFile)->GetFunction()); 173 | 174 | // target->Set(String::NewSymbol("wl_display_interface"), External::Wrap(NULL));//const_cast(&wl_display_interface))); 175 | // 176 | // target->Set(String::NewSymbol("wl_registry_interface"), External::Wrap((void*)&wl_registry_interface)); 177 | // target->Set(String::NewSymbol("wl_callback_interface"), External::Wrap((void*)&wl_callback_interface)); 178 | // target->Set(String::NewSymbol("wl_compositor_interface"), External::Wrap((void*)&wl_compositor_interface)); 179 | // target->Set(String::NewSymbol("wl_shm_pool_interface"), External::Wrap((void*)&wl_shm_pool_interface)); 180 | // target->Set(String::NewSymbol("wl_shm_interface"), External::Wrap((void*)&wl_shm_interface)); 181 | // target->Set(String::NewSymbol("wl_buffer_interface"), External::Wrap((void*)&wl_buffer_interface)); 182 | // target->Set(String::NewSymbol("wl_data_offer_interface"), External::Wrap((void*)&wl_data_offer_interface)); 183 | // target->Set(String::NewSymbol("wl_data_source_interface"), External::Wrap((void*)&wl_data_source_interface)); 184 | // target->Set(String::NewSymbol("wl_data_device_interface"), External::Wrap((void*)&wl_data_device_interface)); 185 | // target->Set(String::NewSymbol("wl_data_device_manager_interface"), External::Wrap((void*)&wl_data_device_manager_interface)); 186 | // target->Set(String::NewSymbol("wl_shell_interface"), External::Wrap((void*)&wl_shell_interface)); 187 | // target->Set(String::NewSymbol("wl_shell_surface_interface"), External::Wrap((void*)&wl_shell_surface_interface)); 188 | // target->Set(String::NewSymbol("wl_surface_interface"), External::Wrap((void*)&wl_surface_interface)); 189 | // target->Set(String::NewSymbol("wl_seat_interface"), External::Wrap((void*)&wl_seat_interface)); 190 | // target->Set(String::NewSymbol("wl_pointer_interface"), External::Wrap((void*)&wl_pointer_interface)); 191 | // target->Set(String::NewSymbol("wl_keyboard_interface"), External::Wrap((void*)&wl_keyboard_interface)); 192 | // target->Set(String::NewSymbol("wl_touch_interface"), External::Wrap((void*)&wl_touch_interface)); 193 | // target->Set(String::NewSymbol("wl_output_interface"), External::Wrap((void*)&wl_output_interface)); 194 | // target->Set(String::NewSymbol("wl_region_interface"), External::Wrap((void*)&wl_region_interface)); 195 | } 196 | NODE_MODULE(wayland_client, Init); 197 | -------------------------------------------------------------------------------- /src/interface.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace v8; 6 | 7 | extern "C" { 8 | #include 9 | #include 10 | #include 11 | } 12 | 13 | #include "interface.h" 14 | 15 | Persistent Interface::constructor; 16 | 17 | void Interface::Init(Handle target) { 18 | Local tpl = FunctionTemplate::New(New); 19 | tpl->SetClassName(String::NewSymbol("Interface")); 20 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 21 | NODE_SET_PROTOTYPE_METHOD(tpl, "get_name", GetName); 22 | constructor = Persistent::New(tpl->GetFunction()); 23 | target->Set(String::NewSymbol("Interface"), constructor); 24 | } 25 | 26 | Handle Interface::New(const Arguments& args) { 27 | HandleScope scope; 28 | Interface* interface = new Interface(); 29 | interface->interface = (const struct wl_interface*)External::Unwrap(args[0]); 30 | interface->Wrap(args.This()); 31 | return args.This(); 32 | }; 33 | Handle Interface::GetName(const Arguments& args) { 34 | HandleScope scope; 35 | const struct wl_interface* interface = Unwrap(args.This()); 36 | return scope.Close(String::New(interface->name)); 37 | } 38 | 39 | static const struct wl_interface* interfaces[] = { 40 | &wl_display_interface, 41 | &wl_registry_interface, 42 | &wl_callback_interface, 43 | &wl_compositor_interface, 44 | &wl_shm_pool_interface, 45 | &wl_shm_interface, 46 | &wl_buffer_interface, 47 | &wl_data_offer_interface, 48 | &wl_data_source_interface, 49 | &wl_data_device_interface, 50 | &wl_data_device_manager_interface, 51 | &wl_shell_interface, 52 | &wl_shell_surface_interface, 53 | &wl_surface_interface, 54 | &wl_seat_interface, 55 | &wl_pointer_interface, 56 | &wl_keyboard_interface, 57 | &wl_touch_interface, 58 | &wl_output_interface, 59 | &wl_region_interface, 60 | NULL, 61 | }; 62 | 63 | static const struct wl_interface* get_interface_by_name(const char* name) { 64 | for (int i = 0; interfaces[i] != NULL; i++) { 65 | if (strcmp(interfaces[i]->name, name) == 0) { return interfaces[i]; } 66 | } 67 | return NULL; 68 | } 69 | 70 | Handle Interface::GetInterfaceByName(const Arguments& args) { 71 | HandleScope scope; 72 | v8::String::AsciiValue string(args[0]); 73 | const struct wl_interface* interface = get_interface_by_name(*string); 74 | if (interface == NULL) return scope.Close(Undefined()); 75 | const unsigned argc = 1; 76 | Handle argv[argc] = { External::Wrap((void*)interface) }; 77 | Local instance = Interface::constructor->NewInstance(argc, argv); 78 | return scope.Close(instance); 79 | } 80 | -------------------------------------------------------------------------------- /src/interface.h: -------------------------------------------------------------------------------- 1 | class Interface : public node::ObjectWrap { 2 | const struct wl_interface* interface; 3 | public: 4 | static inline const struct wl_interface* Unwrap(Handle object) { 5 | Interface* wli = ObjectWrap::Unwrap(object); 6 | return wli->interface; 7 | }; 8 | static void Init(Handle target); 9 | static Handle New(const Arguments& args); 10 | static Handle GetName(const Arguments& args); 11 | static Handle GetInterfaceByName(const Arguments& args); 12 | 13 | static Persistent constructor; 14 | }; 15 | -------------------------------------------------------------------------------- /src/proxy.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace v8; 6 | 7 | extern "C" { 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // for the stub 14 | #include 15 | } 16 | 17 | #include "array.h" 18 | #include "interface.h" 19 | #include "proxy.h" 20 | 21 | Persistent Proxy::constructor; 22 | 23 | void Proxy::Init(Handle target) { 24 | Local tpl = FunctionTemplate::New(New); 25 | tpl->SetClassName(String::NewSymbol("Proxy")); 26 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 27 | NODE_SET_PROTOTYPE_METHOD(tpl, "destroy", Destroy); 28 | NODE_SET_PROTOTYPE_METHOD(tpl, "get_id", GetId); 29 | NODE_SET_PROTOTYPE_METHOD(tpl, "get_class", GetClass); 30 | NODE_SET_PROTOTYPE_METHOD(tpl, "create", Create); 31 | NODE_SET_PROTOTYPE_METHOD(tpl, "listen", Listen); 32 | NODE_SET_PROTOTYPE_METHOD(tpl, "spy", Spy); 33 | NODE_SET_PROTOTYPE_METHOD(tpl, "marshal", Marshal); 34 | constructor = Persistent::New(tpl->GetFunction()); 35 | target->Set(String::NewSymbol("Proxy"), constructor); 36 | } 37 | 38 | Handle Proxy::New(const Arguments& args) { 39 | HandleScope scope; 40 | Proxy* proxy = new Proxy(); 41 | proxy->proxy = (wl_proxy*)External::Unwrap(args[0]); 42 | proxy->interface = (const struct wl_interface*)External::Unwrap(args[1]); 43 | proxy->listener = NULL; 44 | proxy->paddle = (struct paddle*)malloc(sizeof(struct paddle*)); 45 | proxy->paddle->object = Persistent::New(args.This()); 46 | proxy->Wrap(args.This()); 47 | wl_proxy_set_user_data(proxy->proxy, proxy->paddle); 48 | return args.This(); 49 | }; 50 | 51 | void Proxy::Free() { 52 | if (listener) { 53 | listener->value.Dispose(); 54 | free(listener); 55 | listener = NULL; 56 | } 57 | if (paddle) { 58 | paddle->object.Dispose(); 59 | free(paddle); 60 | } 61 | proxy = NULL; 62 | } 63 | 64 | Handle Proxy::Destroy(const Arguments& args) { 65 | HandleScope scope; 66 | Proxy* proxy = AsProxy(args.This()); 67 | if (proxy == NULL) return ThrowException(String::New("proxy is dead")); 68 | wl_proxy_destroy(proxy->proxy); 69 | proxy->Free(); 70 | return scope.Close(Undefined()); 71 | }; 72 | 73 | Handle Proxy::GetId(const Arguments& args) { 74 | HandleScope scope; 75 | Proxy* proxy = AsProxy(args.This()); 76 | if (proxy == NULL) return ThrowException(String::New("proxy is dead")); 77 | uint32_t id = wl_proxy_get_id(proxy->proxy); 78 | return scope.Close(Integer::New(id)); 79 | }; 80 | 81 | Handle Proxy::GetClass(const Arguments& args) { 82 | HandleScope scope; 83 | Proxy* proxy = AsProxy(args.This()); 84 | if (proxy == NULL) return ThrowException(String::New("proxy is dead")); 85 | const char* classname = wl_proxy_get_class(proxy->proxy); 86 | return scope.Close(String::New(classname)); 87 | }; 88 | 89 | Handle Proxy::Create(const Arguments& args) { 90 | HandleScope scope; 91 | Proxy* proxy = AsProxy(args.This()); 92 | if (proxy == NULL) return ThrowException(String::New("proxy is dead")); 93 | const struct wl_interface* interface = Interface::Unwrap(args[0]->ToObject()); 94 | wl_proxy* child_proxy = wl_proxy_create(proxy->proxy, interface); 95 | 96 | const unsigned argc = 2; 97 | Handle argv[argc] = { 98 | External::Wrap(child_proxy), 99 | External::Wrap((void*)interface), 100 | }; 101 | Local instance = Proxy::constructor->NewInstance(argc, argv); 102 | return scope.Close(instance); 103 | } 104 | 105 | Handle Proxy::Listen(const Arguments& args) { 106 | HandleScope scope; 107 | Proxy* proxy = AsProxy(args.This()); 108 | if (proxy == NULL) return ThrowException(String::New("proxy is dead")); 109 | if (proxy->listener != NULL) return ThrowException(String::New("listener added already")); 110 | proxy->listener = (struct listener*)malloc(sizeof(struct listener)); 111 | proxy->listener->value = Persistent::New(args[0]); 112 | wl_proxy_add_dispatcher(proxy->proxy, wl_nodejs_proxy_dispatcher, proxy->listener, proxy->paddle); 113 | return scope.Close(Undefined()); 114 | } 115 | 116 | Handle Proxy::Spy(const Arguments& args) { 117 | HandleScope scope; 118 | Proxy* proxy = AsProxy(args.This()); 119 | if (proxy == NULL) return ThrowException(String::New("proxy is dead")); 120 | proxy->paddle->object.Dispose(); 121 | proxy->paddle->object = Persistent::New(args[0]->ToObject()); 122 | return scope.Close(Undefined()); 123 | } 124 | 125 | static 126 | int wl_argument_from_value(union wl_argument* arg, Local value, int which, int nullable) { 127 | switch (which) { 128 | case 'i': arg->i = value->Int32Value(); break; 129 | case 'u': arg->u = value->Uint32Value(); break; 130 | case 'f': arg->f = wl_fixed_from_double(value->NumberValue()); break; 131 | case 's': 132 | { 133 | // Copy to memory if things break down. 134 | v8::String::Utf8Value string(value); 135 | arg->s = *string; 136 | } break; 137 | case 'o': 138 | case 'n': 139 | { 140 | if (value->IsUndefined() || value->IsNull()) { 141 | arg->o = NULL; 142 | if (!nullable) return 0; 143 | } else { 144 | Proxy* proxy = Proxy::AsProxy(value->ToObject()); 145 | arg->o = (wl_object*)proxy->proxy; 146 | } 147 | } break; 148 | case 'a': 149 | // Remember to unallocate this. 150 | arg->a = (struct wl_array*)malloc(sizeof(struct wl_array)); 151 | arg->a->alloc = 0; 152 | arg->a->data = ArrayBuffer::GetData(value->ToObject(), &arg->a->size); 153 | break; 154 | case 'h': arg->h = value->IntegerValue(); break; 155 | default: 156 | return 0; 157 | } 158 | return 1; 159 | } 160 | 161 | static 162 | void wl_cleanup_argument(union wl_argument* arg, int which) { 163 | switch (which) { 164 | case 'i': break; 165 | case 'u': break; 166 | case 'f': break; 167 | case 's': break; 168 | case 'o': 169 | case 'n': break; 170 | case 'a': free((void*)arg->a); break; 171 | case 'h': break; 172 | } 173 | } 174 | 175 | Local wl_argument_to_value(union wl_argument* arg, int which) { 176 | switch (which) { 177 | case 'i': return Integer::New(arg->i); 178 | case 'u': return Integer::NewFromUnsigned(arg->u); 179 | case 'f': return Number::New(wl_fixed_to_double(arg->f)); 180 | case 's': return String::New(arg->s); 181 | case 'o': 182 | case 'n': { 183 | if (arg->o == NULL) { 184 | return *Null(); 185 | } else { 186 | struct paddle* paddle = (struct paddle*)wl_proxy_get_user_data((wl_proxy*)arg->o); 187 | return *paddle->object; 188 | } 189 | } 190 | case 'a': return ArrayBuffer::New(arg->a->size, arg->a->data); 191 | case 'h': return Integer::New(arg->h); 192 | default: 193 | printf("unknown argument signature=%c\n", which); 194 | assert(false); 195 | } 196 | return *Undefined(); 197 | } 198 | 199 | Handle Proxy::Marshal(const Arguments& args) { 200 | HandleScope scope; 201 | Proxy* proxy = AsProxy(args.This()); 202 | if (proxy == NULL) return ThrowException(String::New("proxy is dead")); 203 | uint32_t opcode = args[0]->Uint32Value(); 204 | const char* signature = proxy->interface->methods[opcode].signature; 205 | int nargs = 0; 206 | for (int i = 0; signature[i]; i++) { 207 | if (signature[i] != '?') nargs++; 208 | } 209 | if (nargs != args.Length() - 1) { 210 | return ThrowException(String::New("argc doesn't match the signature")); 211 | } 212 | union wl_argument *argv = (union wl_argument*)calloc(nargs, sizeof *argv); 213 | int j = 0; 214 | int arg_ok = 1; 215 | for (int i = 0; i < nargs; i++) { 216 | int nullable = 0; 217 | if (signature[j] == '?') { nullable = 1; j++; } 218 | arg_ok &= wl_argument_from_value(argv+i, args[i+1], signature[j++], nullable); 219 | } 220 | if (arg_ok) wl_proxy_marshal_array(proxy->proxy, opcode, argv); 221 | j = 0; 222 | for (int i = 0; i < nargs; i++) { 223 | /*int nullable = 0;*/ 224 | if (signature[j] == '?') { /*nullable = 1;*/ j++; } 225 | wl_cleanup_argument(argv+i, signature[j++]); 226 | } 227 | free(argv); 228 | if (!arg_ok) return ThrowException(String::New("bad argument list")); 229 | return scope.Close(Undefined()); 230 | } 231 | 232 | inline int isnum(char ch) { return ('0' <= ch && ch <= '9'); } 233 | 234 | int 235 | Proxy::wl_nodejs_proxy_dispatcher(const void *data, void *target, uint32_t opcode, 236 | const struct wl_message *message, union wl_argument *args) 237 | { 238 | struct listener* listener = (struct listener*)data; 239 | // printf("proxy data %p with target %p and opcode %i\n", data, target, opcode); 240 | // SelfRef* ref = (SelfRef*)target; 241 | // Proxy* proxy = AsProxy(ref->object); 242 | Persistent callback = Persistent::Cast(listener->value); 243 | 244 | unsigned argc = 1; 245 | 246 | for (int i = 0; message->signature[i] > 0; i++) { 247 | char which = message->signature[i]; 248 | if (which != '?' && !isnum(which)) argc++; 249 | } 250 | 251 | Local argv[argc]; 252 | argv[0] = String::New(message->name); 253 | int j = 0; 254 | for (unsigned i = 0; i < argc-1; i++) { 255 | /*int nullable = 0;*/ 256 | if (message->signature[j] == '?') { /*nullable = 1;*/ j++; } 257 | if (isnum(message->signature[j])) { /*version;*/ j++; } 258 | argv[i+1] = wl_argument_to_value(args+i, message->signature[j++]); 259 | } 260 | callback->Call(Context::GetCurrent()->Global(), argc, argv); 261 | 262 | 263 | 264 | // const char *signature; 265 | // int nargs, nrefs; 266 | // 267 | // jvalue *jargs; 268 | // JNIEnv *env; 269 | // jobject jlistener, jproxy; 270 | // jmethodID mid; 271 | // 272 | // proxy = target; 273 | // 274 | // env = wl_jni_get_env(); 275 | // 276 | // /* Count the number of arguments and references */ 277 | // nargs = 0; 278 | // nrefs = 0; 279 | // for (signature = message->signature; *signature != '\0'; ++signature) { 280 | // switch (*signature) { 281 | // /* These types will require references */ 282 | // case 'f': 283 | // case 's': 284 | // case 'o': 285 | // case 'a': 286 | // case 'n': 287 | // ++nrefs; 288 | // /* These types don't require references */ 289 | // case 'u': 290 | // case 'i': 291 | // case 'h': 292 | // ++nargs; 293 | // break; 294 | // case '?': 295 | // break; 296 | // } 297 | // } 298 | // 299 | // jargs = malloc((nargs + 1) * sizeof *jargs); 300 | // if (jargs == NULL) { 301 | // wl_jni_throw_OutOfMemoryError(env, NULL); 302 | // goto exception_check; 303 | // } 304 | // 305 | // if ((*env)->PushLocalFrame(env, nrefs + 2) < 0) 306 | // goto exception_check; 307 | // 308 | // jproxy = wl_jni_proxy_to_java(env, proxy); 309 | // if ((*env)->ExceptionCheck(env)) { 310 | // goto pop_local_frame; 311 | // } else if (jproxy == NULL) { 312 | // wl_jni_throw_NullPointerException(env, "Proxy should not be null"); 313 | // goto pop_local_frame; 314 | // } 315 | // 316 | // jlistener = (*env)->GetObjectField(env, jproxy, Proxy.listener); 317 | // if ((*env)->ExceptionCheck(env)) 318 | // goto pop_local_frame; 319 | // 320 | // wl_jni_arguments_to_java(env, args, jargs + 1, message->signature, nargs, 321 | // JNI_TRUE, 322 | // (jobject(*)(JNIEnv *, struct wl_object *))&wl_jni_proxy_to_java); 323 | // 324 | // if ((*env)->ExceptionCheck(env)) 325 | // goto pop_local_frame; 326 | // 327 | // jargs[0].l = jproxy; 328 | // mid = ((jmethodID *)data)[opcode]; 329 | // (*env)->CallVoidMethodA(env, jlistener, mid, jargs); 330 | // 331 | //pop_local_frame: 332 | // (*env)->PopLocalFrame(env, NULL); 333 | // free(jargs); 334 | // 335 | //exception_check: 336 | // if ((*env)->ExceptionCheck(env)) 337 | // return -1; 338 | // else 339 | // return 0; 340 | 341 | return 0; 342 | } 343 | -------------------------------------------------------------------------------- /src/proxy.h: -------------------------------------------------------------------------------- 1 | struct paddle { 2 | Persistent object; 3 | }; 4 | 5 | class Proxy : public node::ObjectWrap { 6 | struct listener { 7 | Persistent value; 8 | }; 9 | 10 | static int 11 | wl_nodejs_proxy_dispatcher(const void *data, void *target, uint32_t opcode, 12 | const struct wl_message *message, union wl_argument *args); 13 | struct listener* listener; 14 | public: 15 | static inline Proxy* AsProxy(Handle object){ 16 | Proxy* proxy = ObjectWrap::Unwrap(object); 17 | if (proxy->proxy == NULL) return NULL; 18 | return proxy; 19 | }; 20 | 21 | void Free(); 22 | 23 | static void Init(Handle target); 24 | static Handle New(const Arguments& args); 25 | static Handle Destroy(const Arguments& args); 26 | static Handle GetId(const Arguments& args); 27 | static Handle GetClass(const Arguments& args); 28 | static Handle Create(const Arguments& args); 29 | static Handle Listen(const Arguments& args); 30 | static Handle Spy(const Arguments& args); 31 | static Handle Marshal(const Arguments& args); 32 | 33 | static Persistent constructor; 34 | 35 | const struct wl_interface* interface; 36 | struct wl_proxy* proxy; 37 | struct paddle* paddle; 38 | }; 39 | -------------------------------------------------------------------------------- /tools/nodeland-scanner.py: -------------------------------------------------------------------------------- 1 | from lxml import etree 2 | from sys import stdout, stderr 3 | 4 | def mk_comment(text): 5 | return '/*'+text.strip(' ')+'*/\n' 6 | 7 | header_gen = u"""var wl = require('./build/Release/wayland_client'); 8 | var interfaces = {}; 9 | exports.interfaces = interfaces; 10 | """ 11 | interface_template = u"""function %(name)s(proxy) { 12 | this.proxy = proxy; 13 | proxy.spy(this); 14 | }; 15 | %(name)s.prototype = { 16 | %(prototype)s 17 | }; 18 | %(name)s.interface = wl.get_interface_by_name(%(name)r); 19 | interfaces[%(name)r] = %(name)s 20 | 21 | """ 22 | default_proxy_template = """ listen: function(listeners) { 23 | var self = this; 24 | this.proxy.listen(function(name){ 25 | if (listeners[name]) listeners[name].apply(self, Array.prototype.slice.call(arguments, 1)); 26 | }); 27 | }, 28 | destroy: function() { 29 | this.proxy.destroy() 30 | }""" 31 | request_template = u""" %(name)s: function(%(args)s) { 32 | this.proxy.marshal(%(argv)s); 33 | }""" 34 | factory_template = u""" %(name)s: function(%(args)s) { 35 | new_id = this.proxy.create(%(spy)s.interface); 36 | this.proxy.marshal(%(argv)s); 37 | return new %(spy)s(new_id); 38 | }""" 39 | factory_dyn_template = u""" %(name)s: function(%(args)s) { 40 | new_id = this.proxy.create(spy.interface); 41 | this.proxy.marshal(%(argv)s); 42 | return new spy(new_id); 43 | }""" 44 | 45 | def generate_request(index, request): 46 | data = dict(name = request.attrib['name'], magic=index) 47 | args = [] 48 | argv = [str(index)] 49 | template = request_template 50 | for node in request: 51 | if node.tag == 'arg' and node.attrib['type'] in ('int', 'uint', 'fd', 'string', 'fixed'): 52 | name = node.attrib['name'] 53 | args.append(name) 54 | argv.append(name) 55 | elif node.tag == 'arg' and node.attrib['type'] == 'object': 56 | name = node.attrib['name'] 57 | args.append(name) 58 | argv.append('(%(var)s === null || %(var)s === undefined)?%(var)s:%(var)s.proxy' % dict(var=name)) 59 | elif node.tag == 'arg' and node.attrib['type'] == 'new_id': 60 | if 'interface' in node.attrib: 61 | template = factory_template 62 | data['spy'] = node.attrib['interface'] 63 | argv.append('new_id') 64 | else: 65 | template = factory_dyn_template 66 | args.append('spy, version') 67 | argv.append('spy.interface.get_name(), version, new_id') 68 | elif node.tag == 'description': 69 | continue 70 | else: 71 | stderr.write("%s %r %r" % (node.tag, node.attrib, node[:])) 72 | stderr.write("\n") 73 | raise Exception("unknown argument node %r" % node) 74 | data['args'] = ', '.join(args) 75 | data['argv'] = ', '.join(argv) 76 | return template % data 77 | 78 | def generate_enum_const(enum_name, const): 79 | data = dict( 80 | name=('%s_%s' % (enum_name, const.attrib['name'])).upper(), 81 | value=const.attrib['value'], 82 | ) 83 | return '%(name)s: %(value)s' % data 84 | 85 | def generate_interface(interface): 86 | count = 0 87 | methods = [] 88 | enums = [] 89 | name = interface.attrib['name'] 90 | if name != 'wl_display': 91 | methods.append(default_proxy_template) 92 | for node in interface: 93 | if node.tag == 'description': 94 | continue 95 | elif node.tag == 'request': 96 | methods.append(generate_request(count, node)) 97 | count += 1 98 | elif node.tag == 'event': 99 | continue 100 | elif node.tag == 'enum': 101 | enum_name = node.attrib['name'] 102 | for node in node: 103 | if node.tag == 'entry': 104 | enums.append(generate_enum_const(enum_name, node)) 105 | elif node.tag == 'description': 106 | continue 107 | else: 108 | stderr.write("%s %r %r" % (node.tag, node.attrib, node[:])) 109 | stderr.write("\n") 110 | raise Exception("unknown entry node %r" % node) 111 | elif node.tag == etree.Comment: 112 | continue 113 | else: 114 | raise Exception("unknown interface node %r" % node) 115 | return dict(prototype=',\n'.join(methods + enums)) 116 | 117 | root = etree.parse("wayland.xml").getroot() 118 | stdout.write(header_gen) 119 | for node in root: 120 | if node.tag == 'copyright': 121 | stdout.write(mk_comment(node.text).encode('utf-8')) 122 | elif node.tag == 'interface': 123 | data = generate_interface(node) 124 | data.update(node.attrib) 125 | stdout.write((interface_template % data).encode('utf-8')) 126 | else: 127 | raise Exception("unknown root node") 128 | --------------------------------------------------------------------------------