├── .gitignore ├── .gitmodules ├── Gulpfile.js ├── LICENSE ├── Makefile ├── Vagrantfile ├── autogen ├── generate2.js ├── lib │ ├── enums.js │ ├── errors.js │ ├── events.js │ ├── misc.js │ ├── requests.js │ ├── statement_body.js │ └── types.js └── proto │ ├── bigreq.xml │ ├── composite.xml │ ├── damage.xml │ ├── dpms.xml │ ├── dri2.xml │ ├── ge.xml │ ├── glx.xml │ ├── randr.xml │ ├── record.xml │ ├── render.xml │ ├── res.xml │ ├── screensaver.xml │ ├── shape.xml │ ├── shm.xml │ ├── sync.xml │ ├── xc_misc.xml │ ├── xevie.xml │ ├── xf86dri.xml │ ├── xf86vidmode.xml │ ├── xfixes.xml │ ├── xinerama.xml │ ├── xinput.xml │ ├── xkb.xml │ ├── xprint.xml │ ├── xproto.xml │ ├── xselinux.xml │ ├── xtest.xml │ ├── xv.xml │ └── xvmc.xml ├── basic.js ├── dist ├── Gray ├── blackboxrc └── xserver.conf ├── docs ├── SMlib.pdf ├── X11R7.5 proto.mhtml ├── X11R7.5 proto.pdf ├── XACE-Spec.pdf ├── XI2proto.txt ├── Xserver-spec.pdf ├── Xtrans.pdf ├── XvMC_API.txt ├── bigreq.pdf ├── compositeproto.txt ├── damageproto.txt ├── fixesproto.txt ├── geproto.txt ├── libXrender.txt ├── mit-shm.pdf ├── randrproto.txt ├── record.pdf ├── recordlib.pdf ├── renderproto.txt ├── shape.pdf ├── xdmcp.pdf ├── xim.pdf ├── xsmp.pdf ├── xtest.pdf ├── xtestlib.pdf └── xv-protocol-v2.txt ├── keymap.js ├── keymap.json ├── keymap.txt ├── lib └── fonts.js ├── package.json ├── proxy.js ├── public ├── audio │ └── bell.mp3 ├── check.png ├── fonts ├── index.html ├── require.config.js └── reset.css ├── readme.md ├── rgb.js ├── rgb.txt ├── src ├── common.js ├── elements.js ├── endianbuffer.js ├── event_types.js ├── fs.js ├── keymap.js ├── rgb_colors.js ├── site.js ├── stack_magick.js ├── worker_comms.js ├── worker_console.js ├── x_client.js ├── x_protocol.js ├── x_protocol_new.js ├── x_server.js ├── x_types.js ├── x_types_font.js └── xtypebuffer.js ├── types.js └── x_types.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .project 3 | .settings 4 | *.sublime-workspace 5 | ignored 6 | npm-debug.log 7 | public/common.js 8 | public/common.js.map 9 | public/worker_comms.js 10 | public/worker_comms.js.map 11 | public/site.js 12 | public/site.js.map 13 | tmp/ 14 | .swp 15 | .vagrant/ 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pcf-font-to-vector"] 2 | path = fonts 3 | url = git@github.com:tnkd/pcf-font-to-vector.git 4 | -------------------------------------------------------------------------------- /Gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var source = require('vinyl-source-stream'); 3 | var watch = require('gulp-watch'); 4 | var sourcemaps = require('gulp-sourcemaps'); 5 | var buffer = require('vinyl-buffer'); 6 | var factor = require('factor-bundle'); 7 | var insert = require('gulp-insert'); 8 | 9 | var browserify = require('browserify'); 10 | var babelify = require('babelify'); 11 | 12 | if (process.env.NODE_ENV === undefined) { 13 | process.env.NODE_ENV = 'development'; 14 | } 15 | 16 | gulp.task('js', function () { 17 | var worker_stream = source('./worker_comms.js'); 18 | var b = browserify({ 19 | entries: ['./src/site.js', './src/worker_comms.js'], 20 | debug: true, 21 | fullpath: true, 22 | }) 23 | .transform(babelify.configure({ 24 | sourceMap: true, 25 | optional: [ 26 | 'es7.objectRestSpread', 27 | 'es7.classProperties', 28 | 'es7.exportExtensions', 29 | 'es7.asyncFunctions', 30 | 'es7.functionBind', 31 | 'es7.comprehensions', 32 | 'runtime', 33 | 'utility.inlineEnvironmentVariables', 34 | // 'minification.deadCodeElimination', 35 | 'minification.memberExpressionLiterals', 36 | 'minification.propertyLiterals', 37 | // 'validation.undeclaredVariableCheck', 38 | 'spec.undefinedToVoid', 39 | ], 40 | experimental: true, 41 | blacklist: [ 42 | 'strict', // Blacklisted to allow access to objs in stacktraces 43 | ], 44 | })) 45 | .plugin(factor, {o: [ 46 | './public/site.js', 47 | worker_stream, 48 | ]}); 49 | 50 | worker_stream 51 | .pipe(buffer()) 52 | .pipe(sourcemaps.init({loadMaps: true})) 53 | .pipe(insert.prepend('importScripts("common.js");')) 54 | .pipe(sourcemaps.write('./')) 55 | .pipe(gulp.dest('./public/')); 56 | 57 | return b.bundle() 58 | .pipe(source('./common.js')) 59 | .pipe(buffer()) 60 | .pipe(sourcemaps.init({loadMaps: true})) 61 | .pipe(sourcemaps.write('./')) 62 | .pipe(gulp.dest('./public/')); 63 | }); 64 | 65 | gulp.task('watcher', function () { 66 | gulp.watch(['src/**/*.js', 'Gulpfile.js'], ['js']); 67 | }); 68 | 69 | gulp.task('default', ['js']); 70 | 71 | gulp.task('watch', ['js' ,'watcher']); 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Greg Miell / Tango November Kilo Delta Limited 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of the FreeBSD Project. 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: fonts 2 | 3 | fonts: 4 | cd fonts; mkfontdir; make 5 | find public/fonts -type l -delete 6 | cd public/fonts/; ln -s ../../fonts/out/*.woff . 7 | cd public/fonts/; ln -s ../../fonts/out/*.json . 8 | cd public/fonts/; ln -s ../../fonts/fonts.dir . 9 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure(2) do |config| 5 | # config.vm.box = "ubuntu/ubuntu-15.04-snappy-core-stable" 6 | config.vm.box = "ubuntu/trusty64" 7 | 8 | config.vm.provider "virtualbox" do |vb| 9 | vb.memory = 2048 10 | vb.cpus = 2 11 | end 12 | 13 | # Create a forwarded port mapping which allows access to a specific port 14 | # within the machine from a port on the host machine. In the example below, 15 | # accessing "localhost:8080" will access port 80 on the guest machine. 16 | config.vm.network "forwarded_port", guest: 3000, host: 3080 17 | config.vm.network :forwarded_port, guest: 22, host: 2222, id: "ssh", auto_correct: true 18 | 19 | # Share an additional folder to the guest VM. The first argument is 20 | # the path on the host to the actual folder. The second argument is 21 | # the path on the guest to mount the folder. And the optional third 22 | # argument is a set of non-required options. 23 | config.vm.synced_folder ".", "/mnt/server" 24 | 25 | config.vm.provision "shell", inline: <<-SCRIPT 26 | apt-get install -y build-essential x11-apps x11-utils blackbox blackbox-themes menu git 27 | 28 | sudo -iu vagrant /bin/bash - <<-EOF 29 | cd ~ 30 | 31 | if [ ! -d ~/.nvm ]; then 32 | git clone https://github.com/creationix/nvm.git ~/.nvm 33 | cd ~/.nvm 34 | git checkout $(git describe --abbrev=0 --tags) 35 | cd ~ 36 | fi 37 | 38 | if ! grep -q '. ~/.nvm/nvm.sh' .profile; then 39 | echo '. ~/.nvm/nvm.sh' >> .profile 40 | fi 41 | 42 | . ~/.nvm/nvm.sh 43 | 44 | nvm install node 45 | nvm alias default node 46 | echo "rar \\$(nvm version)" 47 | 48 | nvm ls | grep -v iojs | grep -o -P "v\\d+\\.\\d+\\.\\d+" | sort -u | grep -v "\\$(nvm version)" | while read rmver; do 49 | echo "Uninstall \\$rmver" 50 | nvm uninstall \\$rmver 51 | done 52 | 53 | EOF 54 | 55 | cp /mnt/server/dist/xserver.conf /etc/init/ 56 | 57 | initctl stop xserver 58 | kill -HUP 1 59 | initctl start xserver 60 | 61 | update-menus 62 | sudo -iu vagrant /bin/bash - <<-EOF 63 | cp /mnt/server/dist/blackboxrc /home/vagrant/.blackboxrc 64 | mkdir -p /home/vagrant/.blackbox/styles 65 | cp /mnt/server/dist/Gray /home/vagrant/.blackbox/styles/ 66 | EOF 67 | SCRIPT 68 | 69 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 70 | # such as FTP and Heroku are also available. See the documentation at 71 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 72 | # config.push.define "atlas" do |push| 73 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 74 | # end 75 | 76 | # Enable provisioning with a shell script. Additional provisioners such as 77 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 78 | # documentation for more information about their specific syntax and use. 79 | # config.vm.provision "shell", inline: <<-SHELL 80 | # sudo apt-get update 81 | # sudo apt-get install -y apache2 82 | # SHELL 83 | end 84 | -------------------------------------------------------------------------------- /autogen/generate2.js: -------------------------------------------------------------------------------- 1 | #!node --harmony_destructuring 2 | "use strict"; 3 | 4 | var path = require('path'); 5 | var fs = require('fs'); 6 | var libxml = require('libxmljs'); 7 | var { b: builders } = require('ast-types'); 8 | var recast = require('recast'); 9 | var { Classes } = require('./lib/misc'); 10 | 11 | var doc = libxml.parseXml(fs.readFileSync(path.join(__dirname, 'proto/xproto.xml'), 'ascii')); 12 | 13 | var klasses = new Classes(); 14 | 15 | var klass = klasses.newClass('XTypeBuffer', 'CursorBuffer'); 16 | 17 | require('./lib/types')(doc, klass, klasses); 18 | require('./lib/enums')(doc, klass, klasses); 19 | require('./lib/events')(doc, klass, klasses); 20 | require('./lib/errors')(doc, klass, klasses); 21 | require('./lib/requests')(doc, klass, klasses); 22 | 23 | fs.writeFile(path.join(__dirname, '../src/xtypebuffer.js'), klasses.toString()); 24 | -------------------------------------------------------------------------------- /autogen/lib/enums.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var { builders :b } = require('ast-types'); 3 | var parseBody = require('./statement_body'); 4 | 5 | module.exports = function genErrors(doc, klass, klasses) { 6 | var enums = new Map(); 7 | 8 | for (let _enum of doc.find('enum')) { 9 | let name = _enum.attr('name').value(); 10 | let values = new Map(); 11 | let bits = new Map(); 12 | 13 | if (name === 'Atom') { 14 | // TODO: Bojizzle the Atom Enum 15 | console.error('TODO: Implement Atom Enum'); 16 | continue; 17 | } 18 | 19 | var parent_klass = `${_enum.find('*/bit').length ? 'Bit' : 'Value'}Enum`; 20 | 21 | var enum_klass = klasses.newClass(`${name}Enum`, parent_klass); 22 | enums.set(name, b.identifier(enum_klass.name)); 23 | 24 | for (let item of _enum.find('item')) { 25 | let item_name = item.attr('name').value(); 26 | let child = item.get('*'); 27 | 28 | if (!child) { 29 | console.error('Enum item without a child, wut?!', item_name); 30 | continue; 31 | } 32 | let item_value = parseInt(child.text()); 33 | 34 | switch (child.name()) { 35 | case 'value': 36 | values.set(item_value, item_name); 37 | break; 38 | case 'bit': 39 | bits.set(item_value, item_name); 40 | break; 41 | default: 42 | throw new Error(`Unknown child type ${child.name()}`); 43 | } 44 | } 45 | 46 | if (values.size) { 47 | enum_klass.addProperty('_values', values, true); 48 | } 49 | if (parent_klass === 'BitEnum' && bits.size) { 50 | enum_klass.addProperty('_bits', bits, true); 51 | } 52 | } 53 | 54 | klass.addProperty('enums', enums, true); 55 | } 56 | -------------------------------------------------------------------------------- /autogen/lib/errors.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var { builders :b } = require('ast-types'); 3 | var parseBody = require('./statement_body'); 4 | 5 | module.exports = function genErrors(doc, klass) { 6 | var error_numbers = new Map(); 7 | 8 | for (let error of doc.find('error')) { 9 | // TODO: Create classes & objects 10 | let name = error.attr('name').value(); 11 | let number = parseInt(error.attr('number').value()); 12 | error_numbers.set(number, name); 13 | let [read_stmts, write_stmts] = parseBody(error); 14 | klass.addMethod(`error_read${name}`, [], read_stmts); 15 | klass.addMethod(`error_write${name}`, [b.identifier('obj')], write_stmts); 16 | } 17 | 18 | for (let error of doc.find('errorcopy')) { 19 | // TODO: Create classes & objects 20 | let name = error.attr('name').value(); 21 | let ref = error.attr('ref').value(); 22 | let number = parseInt(error.attr('number').value()); 23 | error_numbers.set(number, name); 24 | klass.addSymRead(`error_read${name}`, `error_read${ref}`); 25 | klass.addSymWrite(`error_write${name}`, `error_write${ref}`); 26 | } 27 | 28 | klass.addProperty('error_numbers', error_numbers, true); 29 | } 30 | -------------------------------------------------------------------------------- /autogen/lib/events.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var { builders :b } = require('ast-types'); 3 | var parseBody = require('./statement_body'); 4 | 5 | module.exports = function genEvents(doc, klass, klasses) { 6 | var event_numbers = new Map(); 7 | 8 | for (let event of doc.find('event')) { 9 | // TODO: Create classes & objects 10 | let name = event.attr('name').value(); 11 | let number = parseInt(event.attr('number').value()); 12 | event_numbers.set(number, name); 13 | let [read_stmts, write_stmts] = parseBody(event, klasses); 14 | klass.addMethod(`event_read${name}`, [], read_stmts); 15 | klass.addMethod(`event_write${name}`, [b.identifier('obj')], write_stmts); 16 | } 17 | 18 | for (let event of doc.find('eventcopy')) { 19 | // TODO: Create classes & objects 20 | let name = event.attr('name').value(); 21 | let ref = event.attr('ref').value(); 22 | let number = parseInt(event.attr('number').value()); 23 | event_numbers.set(number, name); 24 | klass.addSymRead(`event_read${name}`, `event_read${ref}`); 25 | klass.addSymWrite(`event_write${name}`, `event_write${ref}`); 26 | } 27 | 28 | klass.addProperty('event_numbers', event_numbers, true); 29 | } 30 | -------------------------------------------------------------------------------- /autogen/lib/misc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var { builders :b, namedTypes} = require('ast-types'); 3 | var recast = require('recast'); 4 | 5 | function _convertValToAST(value) { 6 | if (Array.isArray(value)) { 7 | return b.arrayExpression(value.map(_convertValToAST)); 8 | } 9 | if (value instanceof Map) { 10 | return b.newExpression( 11 | b.identifier('Map'), 12 | [b.arrayExpression(Array.from(value).map( 13 | (v) => b.arrayExpression(v.map(_convertValToAST))))]); 14 | } 15 | if (value instanceof Set) { 16 | return b.newExpression( 17 | b.identifier('Set'), 18 | [b.arrayExpression(Array.from(value).map(_convertValToAST))]); 19 | } 20 | if (namedTypes.Node.check(value)) { 21 | return value; 22 | } 23 | return b.literal(value); 24 | } 25 | 26 | class Classes { 27 | constructor() { 28 | this.classes = new Map(); 29 | } 30 | newClass(name, extend) { 31 | var klass = new Class(name, extend); 32 | this.classes.set(name, klass); 33 | return klass; 34 | } 35 | getClass(name) { 36 | return this.classes.get(name); 37 | } 38 | program() { 39 | var classes = [ 40 | b.importDeclaration( 41 | [ 42 | b.importSpecifier(b.identifier('ValueEnum')), 43 | b.importSpecifier(b.identifier('BitEnum')), 44 | b.importSpecifier(b.identifier('CursorBuffer')), 45 | ], 46 | b.literal('./endianbuffer') 47 | ), 48 | ]; 49 | for (let [name, klass] of this.classes) { 50 | classes.push(b.exportDeclaration(false, klass.gen())); 51 | } 52 | return b.program(classes); 53 | } 54 | recastPrint() { 55 | return recast.print(this.program(), { tabWidth: 2 }); 56 | } 57 | toString() { 58 | return this.recastPrint().code; 59 | } 60 | } 61 | 62 | class Class { 63 | constructor(name, extend) { 64 | this.name = name; 65 | this.extend = extend; 66 | this.body = []; 67 | } 68 | 69 | addMethod(name, args, body) { 70 | this.body.push(b.methodDefinition( 71 | 'method', 72 | b.identifier(name), 73 | b.functionExpression( 74 | null, 75 | args, 76 | b.blockStatement(body)))); 77 | } 78 | 79 | __addSymMethod(name, callname, read) { 80 | var callArguments = []; 81 | if (!read) { 82 | callArguments = [b.spreadElement(b.identifier('arguments'))]; 83 | } 84 | this.addMethod( 85 | name, 86 | [], 87 | [(read ? b.returnStatement : b.expressionStatement)(b.callExpression( 88 | b.memberExpression( 89 | b.thisExpression(), 90 | b.identifier(callname)), 91 | callArguments 92 | ))]); 93 | } 94 | 95 | addSymRead(name, callname) { 96 | this.__addSymMethod(name, callname, true); 97 | } 98 | 99 | addSymWrite(name, callname) { 100 | this.__addSymMethod(name, callname, false); 101 | } 102 | 103 | addProperty(name, value, is_static) { 104 | this.body.push(b.classProperty( 105 | b.identifier(name), 106 | _convertValToAST(value), 107 | null, 108 | is_static)); 109 | // Add the property here so we can reference it later 110 | // (e.g. enums from statement_body) 111 | this[name] = value; 112 | if (value instanceof Map) { 113 | let inv = this[`${name}_inv`] = new Map(); 114 | for (let [k, v] of value) { 115 | inv.set(v, k); 116 | } 117 | } 118 | } 119 | 120 | gen() { 121 | var extend = null; 122 | if (this.extend) { 123 | extend = b.identifier(this.extend); 124 | } 125 | return b.classDeclaration( 126 | b.identifier(this.name), 127 | b.classBody(this.body), 128 | extend, 129 | []); 130 | } 131 | } 132 | 133 | module.exports = { 134 | Class: Class, 135 | Classes: Classes, 136 | } 137 | -------------------------------------------------------------------------------- /autogen/lib/requests.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var { builders :b } = require('ast-types'); 3 | var parseBody = require('./statement_body'); 4 | 5 | module.exports = function genRequests(doc, klass, klasses) { 6 | var request_opcodes = new Map(); 7 | for (let request of doc.find('request')) { 8 | // TODO: Classes & objects 9 | let name = request.attr('name').value(); 10 | let opcode = parseInt(request.attr('opcode').value()); 11 | request_opcodes.set(opcode, name); 12 | let [read_stmts, write_stmts] = parseBody(request, klasses); 13 | klass.addMethod(`request_read${name}`, [], read_stmts); 14 | klass.addMethod(`request_write${name}`, [b.identifier('obj')], write_stmts); 15 | 16 | let reply = request.get('reply'); 17 | if (reply) { 18 | // TODO: Classes & objects 19 | let [read_stmts, write_stmts] = parseBody(reply, klasses); 20 | klass.addMethod(`reply_read${name}`, [], read_stmts); 21 | klass.addMethod(`reply_write${name}`, [b.identifier('obj')], write_stmts); 22 | } 23 | } 24 | 25 | klass.addProperty('request_opcodes', request_opcodes, true); 26 | } 27 | -------------------------------------------------------------------------------- /autogen/lib/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var { builders :b } = require('ast-types'); 3 | var parseBody = require('./statement_body'); 4 | 5 | module.exports = function genTypes(doc, klass, klasses) { 6 | var types = new Map([ 7 | ['BOOL', 'UInt8'], 8 | ['BYTE', 'UInt8'], 9 | ['CARD8', 'UInt8'], 10 | ['INT8', 'Int8'], 11 | ['CARD16', 'UInt16'], 12 | ['INT16', 'Int16'], 13 | ['CARD32', 'UInt32'], 14 | ['INT32', 'Int32'], 15 | ]); 16 | 17 | var xids = new Map(); 18 | 19 | for (let [name, type] of types) { 20 | klass.addSymRead(`read${name}`, `read${type}`); 21 | klass.addSymWrite(`write${name}`, `write${type}`); 22 | } 23 | 24 | for (let def of doc.find('typedef')) { 25 | let newname = def.attr('newname').value(); 26 | let oldname = def.attr('oldname').value(); 27 | klass.addSymRead(`read${newname}`, `read${oldname}`); 28 | klass.addSymWrite(`write${newname}`, `write${oldname}`); 29 | } 30 | 31 | for (let xid of doc.find('xidtype')) { 32 | let name = xid.attr('name').value(); 33 | xids.set(name, null); 34 | klass.addSymRead(`read${name}`, 'readCARD32'); 35 | klass.addSymWrite(`write${name}`, 'writeCARD32'); 36 | } 37 | 38 | for (let xid of doc.find('xidunion')) { 39 | let name = xid.attr('name').value(); 40 | let _types = xid.find('type').map(t => t.text()); 41 | xids.set(name, new Set(_types)); 42 | types.set(name, 'UInt32'); 43 | klass.addSymRead(`read${name}`, 'readCARD32', false); 44 | klass.addSymWrite(`write${name}`, 'writeCARD32'); 45 | } 46 | 47 | klass.addProperty('xids', xids, true); 48 | 49 | for (let struct of doc.find('struct')) { 50 | let name = struct.attr('name').value(); 51 | let [read_stmts, write_stmts] = parseBody(struct, klasses); 52 | klass.addMethod(`read${name}`, [], read_stmts); 53 | klass.addMethod(`write${name}`, [b.identifier('obj')], write_stmts); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /autogen/proto/bigreq.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /autogen/proto/composite.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 31 | xproto 32 | xfixes 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /autogen/proto/damage.xml: -------------------------------------------------------------------------------- 1 | 2 | 29 | 30 | 32 | xproto 33 | xfixes 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /autogen/proto/dpms.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /autogen/proto/dri2.xml: -------------------------------------------------------------------------------- 1 | 2 | 29 | 30 | 32 | xproto 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 1 55 | 2 56 | 3 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | driver_name_length 95 | 96 | 97 | 98 | 99 | 100 | driver_name_length 101 | 3 102 | 103 | 104 | 3 105 | 106 | 107 | driver_name_length 108 | 109 | 110 | 111 | device_name_length 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | count 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | count 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /autogen/proto/ge.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /autogen/proto/record.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 0 65 | 1 66 | 2 67 | 68 | 69 | 70 | 71 | 1 72 | 2 73 | 3 74 | 75 | 76 | 77 | 78 | 79 | 80 | num_ranges 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | num_client_specs 108 | 109 | 110 | num_ranges 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | num_client_specs 122 | 123 | 124 | num_ranges 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | num_client_specs 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | num_intercepted_clients 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | length 164 | 4 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /autogen/proto/res.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 30 | xproto 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | num_clients 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | num_types 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /autogen/proto/screensaver.xml: -------------------------------------------------------------------------------- 1 | 28 | 32 | 33 | 34 | 35 | 36 | xproto 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 0 46 | 1 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /autogen/proto/shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 30 | xproto 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | rectangles_len 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /autogen/proto/shm.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 30 | xproto 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /autogen/proto/sync.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 9 | xproto 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 0 37 | 1 38 | 2 39 | 3 40 | 4 41 | 5 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | name_len 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | counters_len 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 | 142 | 143 | 144 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /autogen/proto/xc_misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ids_len 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /autogen/proto/xevie.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /autogen/proto/xf86dri.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | bus_id_len 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | client_driver_name_len 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | num_clip_rects 140 | 141 | 142 | num_back_clip_rects 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | device_private_size 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /autogen/proto/xfixes.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | xproto 31 | render 32 | shape 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 0 77 | 1 78 | 2 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 0 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | width 133 | height 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | length 234 | 2 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | nbytes 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | nbytes 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | nbytes 295 | 296 | 297 | width 298 | height 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | nbytes 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | -------------------------------------------------------------------------------- /autogen/proto/xinerama.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 31 | 32 | 33 | 34 | xproto 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | number 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /autogen/proto/xselinux.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 27 | xproto 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | context_len 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | context_len 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | context_len 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | context_len 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | context_len 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | context_len 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | context_len 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | object_context_len 113 | 114 | 115 | data_context_len 116 | 117 | 118 | 119 | 120 | 121 | 122 | context_len 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | context_len 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | context_len 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | context_len 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | context_len 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | context_len 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | properties_len 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | context_len 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | context_len 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | context_len 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | context_len 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | context_len 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | context_len 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | selections_len 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | context_len 272 | 273 | 274 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /autogen/proto/xtest.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 31 | 32 | 33 | xproto 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 0 48 | 1 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /autogen/proto/xvmc.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 30 | xv 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | num 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | length 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | length 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 4 121 | 122 | 123 | 124 | length 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | num 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /dist/Gray: -------------------------------------------------------------------------------- 1 | *button.pressed.appearance: sunken solid 2 | *button.pressed.backgroundColor: darkgrey 3 | *button.appearance: parentrelative 4 | *button.foregroundColor: black 5 | 6 | *backgroundColor: rgb:dd/dd/d7 7 | *color1: rgb:dd/dd/d7 8 | *color2: rgb:ee/ee/e7 9 | 10 | *font: -misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso8859-1 11 | 12 | *borderColor: black 13 | *borderWidth: 1 14 | *marginWidth: 2 15 | 16 | menu.title.appearance: flat solid border 17 | menu.title.backgroundColor: grey 18 | menu.title.foregroundColor: black 19 | menu.title.textColor: black 20 | menu.title.alignment: center 21 | menu.title.font: -misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso8859-1 22 | 23 | menu.frame.appearance: raised gradient border 24 | menu.frame.textColor: black 25 | menu.frame.foregroundColor: grey40 26 | menu.frame.disabledColor: darkgrey 27 | menu.frame.alignment: left 28 | 29 | menu.active.appearance: flat solid border 30 | menu.active.backgroundColor: rgb:77/77/74 31 | menu.active.foregroundColor: white 32 | menu.active.textColor: white 33 | 34 | rootCommand: bsetroot -solid rgb:aa/aa/aa 35 | 36 | slit.appearance: raised solid 37 | slit.backgroundColor: rgb:aa/aa/aa 38 | slit.marginWidth: 3 39 | 40 | toolbar.appearance: raised gradient border 41 | toolbar.alignment: center 42 | toolbar*textColor: black 43 | toolbar.label.appearance: parentrelative 44 | toolbar.clock.appearance: parentrelative 45 | toolbar.windowLabel.appearance: parentrelative 46 | 47 | window.title.focus.appearance: raised gradient border 48 | window.title.focus.color1: rgb:66/66/63 49 | window.title.focus.color2: rgb:88/88/84 50 | window.title.unfocus.appearance: raised gradient border 51 | 52 | window.label.focus.appearance: parentrelative 53 | window.label.focus.textColor: white 54 | window.label.unfocus.appearance: parentrelative 55 | window.label.unfocus.textColor: darkgrey 56 | window.label.marginWidth: 1 57 | 58 | window.button.focus.appearance: parentrelative 59 | window.button.focus.foregroundColor: white 60 | 61 | window.button.unfocus.appearance: parentrelative 62 | window.button.unfocus.foregroundColor: darkgrey 63 | 64 | window.handle.focus.appearance: raised solid border 65 | window.handle.unfocus.appearance: raised solid border 66 | 67 | window.grip.focus.appearance: raised solid border 68 | window.grip.focus.backgroundColor: rgb:77/77/74 69 | window.grip.unfocus.appearance: raised solid border 70 | 71 | window*alignment: center 72 | window.font: -misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso8859-1 73 | window.handleHeight: 8 74 | -------------------------------------------------------------------------------- /dist/blackboxrc: -------------------------------------------------------------------------------- 1 | session.styleFile: /home/vagrant/.blackbox/styles/Gray 2 | session.screen0.slit.placement: CenterRight 3 | session.screen0.slit.direction: Vertical 4 | session.screen0.slit.onTop: False 5 | session.screen0.slit.autoHide: False 6 | session.screen0.toolbar.onTop: False 7 | session.screen0.toolbar.autoHide: False 8 | session.screen0.toolbar.placement: BottomCenter 9 | session.screen0.toolbar.widthPercent: 66 10 | session.screen0.enableToolbar: True 11 | session.screen0.workspaces: 4 12 | session.screen0.workspaceNames: Workspace 1,Workspace 2,Workspace 3,Workspace 4 13 | session.screen0.strftimeFormat: %I:%M %p 14 | session.windowSnapThreshold: 0 15 | session.autoRaiseDelay: 400 16 | session.placementIgnoresShaded: True 17 | session.focusLastWindow: True 18 | session.opaqueMove: True 19 | session.changeWorkspaceWithMouseWheel: True 20 | session.imageDither: OrderedDither 21 | session.windowPlacement: RowSmartPlacement 22 | session.shadeWindowWithMouseWheel: True 23 | session.opaqueResize: True 24 | session.toolbarActionsWithMouseWheel: True 25 | session.rowPlacementDirection: LeftToRight 26 | session.maximumColors: 0 27 | session.disableBindingsWithScrollLock: False 28 | session.fullMaximization: False 29 | session.colPlacementDirection: TopToBottom 30 | session.doubleClickInterval: 250 31 | session.edgeSnapThreshold: 0 32 | session.focusNewWindows: True 33 | session.focusModel: ClickToFocus 34 | -------------------------------------------------------------------------------- /dist/xserver.conf: -------------------------------------------------------------------------------- 1 | description "Play server" 2 | start on vagrant-mounted 3 | 4 | respawn 5 | respawn limit unlimited 6 | 7 | setuid vagrant 8 | 9 | script 10 | cd /mnt/server/ 11 | export HOME=/home/vagrant 12 | export NVM_DIR=/home/vagrant/.nvm 13 | /home/vagrant/.nvm/nvm-exec npm start 2>&1 > /mnt/server/tmp/server.log 14 | sleep 5 15 | end script 16 | -------------------------------------------------------------------------------- /docs/SMlib.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/SMlib.pdf -------------------------------------------------------------------------------- /docs/X11R7.5 proto.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/X11R7.5 proto.pdf -------------------------------------------------------------------------------- /docs/XACE-Spec.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/XACE-Spec.pdf -------------------------------------------------------------------------------- /docs/Xserver-spec.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/Xserver-spec.pdf -------------------------------------------------------------------------------- /docs/Xtrans.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/Xtrans.pdf -------------------------------------------------------------------------------- /docs/bigreq.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/bigreq.pdf -------------------------------------------------------------------------------- /docs/damageproto.txt: -------------------------------------------------------------------------------- 1 | The DAMAGE Extension 2 | Protocol Version 1.1 3 | Document Revision 1 4 | 2007-01-08 5 | 6 | Keith Packard 7 | keithp@keithp.com 8 | 9 | Eric Anholt 10 | eric@anholt.net 11 | Open Source Technology Center 12 | Intel Corporation 13 | 1. Introduction 14 | 15 | Monitoring the regions affected by rendering has wide-spread use, from 16 | VNC-like systems scraping the screen to screen magnifying applications 17 | designed to aid users with limited visual acuity. The DAMAGE extension is 18 | designed to make such applications reasonably efficient in the face of 19 | server-client latency. 20 | 21 | 2. Acknolwedgements 22 | 23 | As usual, the author had significant input from many people, in particular: 24 | 25 | + Havoc Pennington who designed and implemented a Damage extension 26 | last year which was then lost to the mists of time. 27 | 28 | + Bill Haneman whose work on accessibility in the Gnome environment 29 | is legendary. 30 | 31 | + Jim Gettys who found a way to avoid streaming damage rectangles 32 | to the client in many cases. 33 | 34 | + Owen Taylor who suggested that streaming damage rectangles may 35 | be warranted in some cases after all. 36 | 37 | 3. Damage Model 38 | 39 | We call changes made to pixel contents of windows and pixmaps 'damage' 40 | throughout this extension. Another notion of 'damage' are drawable regions 41 | which are in need of redisplay to repair the effects of window manipulation 42 | or other data loss. This extension doesn't deal with this second notion at 43 | all; suggestions on a better term which isn't easily conflated with existing 44 | notions are eagerly solicited. 45 | 46 | Damage accumulates as drawing occurs in the drawable. Each drawing operation 47 | 'damages' one or more rectangular areas within the drawable. The rectangles 48 | are guaranteed to include the set of pixels modified by each operation, but 49 | may include significantly more than just those pixels. The desire is for 50 | the damage to strike a balance between the number of rectangles reported and 51 | the extraneous area included. A reasonable goal is for each primitive 52 | object drawn (line, string, rectangle) to be represented as a single 53 | rectangle and for the damage area of the operation to be the union of these 54 | rectangles. 55 | 56 | The DAMAGE extension allows applications to either receive the raw 57 | rectangles as a stream of events, or to have them partially processed within 58 | the X server to reduce the amount of data transmitted as well as reduce the 59 | processing latency once the repaint operation has started. 60 | 61 | Damage to a window reflects both drawing within the window itself as well as 62 | drawing within any inferior window that affects pixels seen by 63 | IncludeInferiors rendering operations. To reduce the computational 64 | complexity of this, the DAMAGE extension allows the server to monitor all 65 | rendering operations within the physical target pixel storage that fall 66 | within the bounds of the window. In a system with a single frame buffer 67 | holding all windows, this means that damage will accumulate for all 68 | rendering operations that lie within the visible part of the window. 69 | 70 | The precise reason for this architecture is to enable the Composite 71 | extension which provides multiple pixel storage areas for the screen 72 | contents. 73 | 74 | 3.1 Additions in the 1.1 version of the protocol 75 | 76 | Damage is automatically computed by the X Server for X rendering operations, 77 | but direct rendering extensions have allowed clients to perform rendering 78 | outside of the control of the X Server. The 1.1 version of the protocol 79 | added a request to allow direct rendering clients to report damage to a 80 | drawable. Some direct rendering clients, due to architectural limitations, 81 | always perform rendering to the root window, even in when it should be 82 | performed to the backing pixmap in the Composite case. To provide 83 | less-incorrect rendering in this cases, the direct rendering client should 84 | translate its damage region to screen coordinates and report the damage against 85 | the root window rather than the drawable. 86 | 87 | 4. Data types 88 | 89 | The "Damage" object holds any accumulated damage region and reflects the 90 | relationship between the drawable selected for damage notification and the 91 | drawable for which damage is tracked. 92 | 93 | 5. Errors 94 | 95 | Damage 96 | A value for a DAMAGE argument does not name a defined DAMAGE. 97 | 98 | 6. Types 99 | 100 | DAMAGE 32-bit value (top three bits guaranteed to be zero) 101 | 102 | DamageReportLevel { DamageReportRawRectangles, 103 | DamageReportDeltaRectangles, 104 | DamageReportBoundingBox, 105 | DamageReportNonEmpty } 106 | 107 | DamageReportRawRectangles 108 | 109 | Delivers DamageNotify events each time the screen 110 | is modified with rectangular bounds that circumscribe 111 | the damaged area. No attempt to compress out overlapping 112 | rectangles is made. 113 | 114 | DamageReportDeltaRectangles 115 | 116 | Delivers DamageNotify events each time damage occurs 117 | which is not included in the damage region. The 118 | reported rectangles include only the changes to that 119 | area, not the raw damage data. 120 | 121 | DamageReportBoundingBox 122 | 123 | Delivers DamageNotify events each time the bounding 124 | box enclosing the damage region increases in size. 125 | The reported rectangle encloses the entire damage region, 126 | not just the changes to that size. 127 | 128 | DamageReportNonEmpty 129 | 130 | Delivers a single DamageNotify event each time the 131 | damage rectangle changes from empty to non-empty, and 132 | also whenever the result of a DamageSubtract request 133 | results in a non-empty region. 134 | 135 | 7. Events 136 | 137 | DamageNotify 138 | 139 | level: DamageReportLevel 140 | drawable: Drawable 141 | damage: DAMAGE 142 | more: Bool 143 | timestamp: Timestamp 144 | area: Rectangle 145 | drawable-geometry: Rectangle 146 | 147 | 'more' indicates whether there are subsequent damage events 148 | being delivered immediately as part of a larger damage region 149 | 150 | 8. Extension Initialization 151 | 152 | The client must negotiate the version of the extension before executing 153 | extension requests. Otherwise, the server will return BadRequest for any 154 | operations other than QueryVersion. 155 | 156 | QueryVersion 157 | 158 | client-major-version: CARD32 159 | client-minor-version: CARD32 160 | 161 | -> 162 | 163 | major-version: CARD32 164 | minor-version: CARD32 165 | 166 | The client sends the highest supported version to the server and 167 | the server sends the highest version it supports, but no higher than 168 | the requested version. Major versions changes can introduce 169 | incompatibilities in existing functionality, minor version 170 | changes introduce only backward compatible changes. It is 171 | the clients responsibility to ensure that the server supports 172 | a version which is compatible with its expectations. Servers 173 | are encouraged to support multiple versions of the extension. 174 | 175 | 9. Enable Monitoring 176 | 177 | DamageCreate 178 | 179 | damage: DAMAGE 180 | drawable: Drawable 181 | level: DamageReportLevel 182 | 183 | Creates a damage object to monitor changes to Drawable 184 | 185 | DamageDestroy 186 | damage: DAMAGE 187 | 188 | Destroys damage. 189 | 190 | DamageSubtract 191 | 192 | damage: DAMAGE 193 | repair: Region or None 194 | parts: Region or None 195 | 196 | Synchronously modifies the regions in the following manner: 197 | 198 | If repair is None: 199 | 200 | 1) if parts is not None, parts = damage 201 | 2) damage = 202 | 203 | Otherwise: 204 | 205 | 1) tmp = damage INTERSECT repair 206 | 2) damage = damage - tmp 207 | 3) if parts is not None, parts = tmp 208 | 4) Generate DamageNotify for remaining damage areas 209 | 210 | DamageAdd 211 | 212 | drawable: Drawable 213 | region: Region 214 | 215 | Reports damage of the region within the given drawable. This may be 216 | used by direct rendering clients to report damage that the server would 217 | otherwise be unaware of. The damage region is relative to the origin 218 | of the drawable. 219 | 220 | Damage posted in this way will appear in DamageNotify events as normal, 221 | and also in server internal damage tracking (for shadow framebuffer 222 | updates, pixmap damage, and other uses). 223 | -------------------------------------------------------------------------------- /docs/geproto.txt: -------------------------------------------------------------------------------- 1 | X Generic Event Extension 2 | Peter Hutterer 3 | peter.hutterer@who-t.net 4 | 5 | 6 | 1. Introduction 7 | 2. Extension Initialization 8 | 3. Extension Events 9 | 4. Notes 10 | 11 | _____________________________________________________________________________ 12 | 1. Introduction 13 | 14 | X was designed to provide 64 event opcodes for all extensions. These events 15 | are limited to 32 bytes. 16 | 17 | The Generic Event Extension provides a template event for extensions to re-use 18 | a single event opcode. GE only provide headers and the most basic 19 | functionality, leaving the extensions to interpret the events in their 20 | specific context. 21 | 22 | GenericEvents may be longer than 32 bytes. If so, the number of 4 byte units 23 | following the initial 32 bytes must be specified in the length field of the 24 | event. 25 | _____________________________________________________________________________ 26 | 2. Extension Initialization 27 | 28 | The name of this extension is "Generic Event Extension" 29 | 30 | ┌─── 31 | GEQueryVersion 32 | client-major-version: CARD16 33 | client-minor-version: CARD16 34 | ▶ 35 | major-version: CARD16 36 | minor-version: CARD16 37 | └─── 38 | 39 | The client sends the highest supported version to the server 40 | and the server sends the highest version it supports, but no 41 | higher than the requested version. Major versions changes can 42 | introduce incompatibilities in existing functionality, minor 43 | version changes introduce only backward compatible changes. 44 | It is the clients responsibility to ensure that the server 45 | supports a version which is compatible with its expectations. 46 | 47 | 48 | As of version 1.0, no other requests are provided by this extension. 49 | _____________________________________________________________________________ 50 | 3. Extension Events 51 | 52 | GE defines a single event, to be used by all extensions. The event's structure 53 | is similar to a reply. 54 | 55 | ┌─── 56 | GenericEvent 57 | type: BYTE; always GenericEvent 58 | extension: CARD8; extension offset 59 | sequenceNumber: CARD16 low 16 bits of request seq. number 60 | length: CARD32 length 61 | evtype: CARD16 event type 62 | └─── 63 | 64 | The field 'extension' is to be set to the major opcode of the 65 | extension. The 'evtype' field is the actual opcode of the event. 66 | The length field specifies the number of 4-byte blocks after the 67 | initial 32 bytes. If length is 0, the event is 32 bytes long. 68 | _____________________________________________________________________________ 69 | 4. Notes 70 | 71 | Although the wire event is of arbitrary length, the actual size of an XEvent 72 | is restricted to sizeof(XEvent) [96 bytes, see Xlib.h]. If an extension 73 | converts a wire event to an XEvent > 96 bytes, it will overwrite the space 74 | allocated for the event. See struct _XSQEvent in Xlibint.h for details. 75 | 76 | Extensions need to malloc additional data and fill the XEvent structure with 77 | pointers to the malloc'd data. The client then needs to free the data, only 78 | the XEvent structure will be released by Xlib. 79 | 80 | The server must not send GenericEvents longer than 32 bytes until it has 81 | verified that the client is able to interpret these events. If a long event is 82 | sent to a client unable to process GenericEvents, future interpretation of 83 | replies and events by this client will fail. 84 | -------------------------------------------------------------------------------- /docs/mit-shm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/mit-shm.pdf -------------------------------------------------------------------------------- /docs/record.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/record.pdf -------------------------------------------------------------------------------- /docs/recordlib.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/recordlib.pdf -------------------------------------------------------------------------------- /docs/shape.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/shape.pdf -------------------------------------------------------------------------------- /docs/xdmcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/xdmcp.pdf -------------------------------------------------------------------------------- /docs/xim.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/xim.pdf -------------------------------------------------------------------------------- /docs/xsmp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/xsmp.pdf -------------------------------------------------------------------------------- /docs/xtest.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/xtest.pdf -------------------------------------------------------------------------------- /docs/xtestlib.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/docs/xtestlib.pdf -------------------------------------------------------------------------------- /keymap.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var lines = fs.readFileSync('keymap.txt', 'utf-8').split('\n'); 4 | 5 | 6 | var keymap = []; 7 | 8 | var mods = ['shift', 'altgr', 'control', 'alt', 'shiftl', 'shiftr', 'ctrll', 'ctrlr', 'capsshift'] 9 | 10 | function key_mapper (list) { 11 | return list.reduce( 12 | function (o, v) { 13 | return o | ((~mods.indexOf(v)) ? (1 << mods.indexOf(v)) : 0); 14 | } 15 | , 0 16 | ) 17 | } 18 | 19 | lines.forEach(function (line) { 20 | if (/^\s+/.test(line)) 21 | return; 22 | line = line.match(/^\s*((?:\s*?(?:\w+))*)\s*keycode\s+(\d+)\s+=\s+((?:(?:[\w+_\-]+)+\s*?)+)\s*$/); 23 | if (!line) 24 | return; 25 | var mods = line[1].replace(/\s+/g, ' ').split(' ') 26 | , code = line[2] * 1 27 | , maps = line[3].replace(/\s+/g, ' ').split(' '); 28 | 29 | var mapped = keymap[code] = keymap[code] || {}; 30 | 31 | mapped[key_mapper(mods)] = maps[0]; 32 | if (maps[1]) 33 | mapped[key_mapper(mods.concat('shift'))] = maps[1]; 34 | if (maps[2]) 35 | mapped[key_mapper(mods.concat('altgr'))] = maps[2]; 36 | if (maps[3]) 37 | mapped[key_mapper(mods.concat('control'))] = maps[3]; 38 | 39 | }); 40 | console.log(JSON.stringify(keymap, null, 2)); 41 | //console.log(keymap); 42 | -------------------------------------------------------------------------------- /keymap.json: -------------------------------------------------------------------------------- 1 | [ 2 | null, 3 | { 4 | "0": "Escape" 5 | }, 6 | { 7 | "0": "one", 8 | "1": "exclam" 9 | }, 10 | { 11 | "0": "two", 12 | "1": "quotedbl" 13 | }, 14 | { 15 | "0": "three" 16 | }, 17 | { 18 | "0": "four", 19 | "1": "dollar" 20 | }, 21 | { 22 | "0": "five", 23 | "1": "percent" 24 | }, 25 | { 26 | "0": "six", 27 | "1": "asciicircum" 28 | }, 29 | { 30 | "0": "seven", 31 | "1": "ampersand", 32 | "2": "braceleft" 33 | }, 34 | { 35 | "0": "eight", 36 | "1": "asterisk", 37 | "2": "bracketleft" 38 | }, 39 | { 40 | "0": "nine", 41 | "1": "parenleft", 42 | "2": "bracketright" 43 | }, 44 | { 45 | "0": "zero", 46 | "1": "parenright", 47 | "2": "braceright" 48 | }, 49 | { 50 | "0": "minus", 51 | "1": "underscore", 52 | "2": "backslash" 53 | }, 54 | { 55 | "0": "equal" 56 | }, 57 | { 58 | "0": "Delete" 59 | }, 60 | { 61 | "0": "Tab" 62 | }, 63 | { 64 | "0": "+q", 65 | "1": "+Q", 66 | "2": "at" 67 | }, 68 | { 69 | "0": "+w", 70 | "1": "+W" 71 | }, 72 | { 73 | "0": "+e" 74 | }, 75 | { 76 | "0": "+r", 77 | "1": "+R" 78 | }, 79 | { 80 | "0": "+t", 81 | "1": "+T" 82 | }, 83 | { 84 | "0": "+y", 85 | "1": "+Y" 86 | }, 87 | { 88 | "0": "+u", 89 | "1": "+U" 90 | }, 91 | { 92 | "0": "+i", 93 | "1": "+I" 94 | }, 95 | { 96 | "0": "+o" 97 | }, 98 | { 99 | "0": "+p" 100 | }, 101 | { 102 | "0": "bracketleft" 103 | }, 104 | { 105 | "0": "bracketright" 106 | }, 107 | { 108 | "0": "Return" 109 | }, 110 | { 111 | "0": "Control" 112 | }, 113 | { 114 | "0": "+a" 115 | }, 116 | { 117 | "0": "+s", 118 | "1": "+S", 119 | "2": "+ssharp" 120 | }, 121 | { 122 | "0": "+d" 123 | }, 124 | { 125 | "0": "+f", 126 | "1": "+F" 127 | }, 128 | { 129 | "0": "+g", 130 | "1": "+G" 131 | }, 132 | { 133 | "0": "+h", 134 | "1": "+H" 135 | }, 136 | { 137 | "0": "+j", 138 | "1": "+J" 139 | }, 140 | { 141 | "0": "+k", 142 | "1": "+K" 143 | }, 144 | { 145 | "0": "+l", 146 | "1": "+L" 147 | }, 148 | { 149 | "0": "semicolon" 150 | }, 151 | { 152 | "0": "apostrophe" 153 | }, 154 | { 155 | "0": "grave" 156 | }, 157 | { 158 | "0": "Shift" 159 | }, 160 | { 161 | "0": "numbersign" 162 | }, 163 | { 164 | "0": "+z", 165 | "1": "+Z" 166 | }, 167 | { 168 | "0": "+x", 169 | "1": "+X" 170 | }, 171 | { 172 | "0": "+c", 173 | "1": "+C" 174 | }, 175 | { 176 | "0": "+v", 177 | "1": "+V" 178 | }, 179 | { 180 | "0": "+b", 181 | "1": "+B" 182 | }, 183 | { 184 | "0": "+n" 185 | }, 186 | { 187 | "0": "+m" 188 | }, 189 | { 190 | "0": "comma", 191 | "1": "less" 192 | }, 193 | { 194 | "0": "period", 195 | "1": "greater" 196 | }, 197 | { 198 | "0": "slash", 199 | "1": "question" 200 | }, 201 | { 202 | "0": "Shift" 203 | }, 204 | { 205 | "0": "KP_Multiply" 206 | }, 207 | { 208 | "0": "Alt" 209 | }, 210 | { 211 | "0": "space" 212 | }, 213 | { 214 | "0": "CtrlL_Lock" 215 | }, 216 | { 217 | "0": "F1" 218 | }, 219 | { 220 | "0": "F2" 221 | }, 222 | { 223 | "0": "F3" 224 | }, 225 | { 226 | "0": "F4" 227 | }, 228 | { 229 | "0": "F5" 230 | }, 231 | { 232 | "0": "F6" 233 | }, 234 | { 235 | "0": "F7" 236 | }, 237 | { 238 | "0": "F8" 239 | }, 240 | { 241 | "0": "F9" 242 | }, 243 | { 244 | "0": "F10" 245 | }, 246 | { 247 | "0": "Num_Lock" 248 | }, 249 | { 250 | "0": "Scroll_Lock" 251 | }, 252 | { 253 | "0": "KP_7" 254 | }, 255 | { 256 | "0": "KP_8" 257 | }, 258 | { 259 | "0": "KP_9" 260 | }, 261 | { 262 | "0": "KP_Subtract" 263 | }, 264 | { 265 | "0": "KP_4" 266 | }, 267 | { 268 | "0": "KP_5" 269 | }, 270 | { 271 | "0": "KP_6" 272 | }, 273 | { 274 | "0": "KP_Add" 275 | }, 276 | { 277 | "0": "KP_1" 278 | }, 279 | { 280 | "0": "KP_2" 281 | }, 282 | { 283 | "0": "KP_3" 284 | }, 285 | { 286 | "0": "KP_0" 287 | }, 288 | { 289 | "0": "KP_Period" 290 | }, 291 | { 292 | "0": "Last_Console", 293 | "1": "Last_Console", 294 | "2": "Last_Console" 295 | }, 296 | null, 297 | { 298 | "0": "backslash", 299 | "1": "bar", 300 | "2": "bar" 301 | }, 302 | { 303 | "0": "F11" 304 | }, 305 | { 306 | "0": "F12" 307 | }, 308 | null, 309 | null, 310 | null, 311 | null, 312 | null, 313 | null, 314 | null, 315 | { 316 | "0": "KP_Enter" 317 | }, 318 | { 319 | "0": "Control" 320 | }, 321 | { 322 | "0": "KP_Divide" 323 | }, 324 | null, 325 | { 326 | "0": "AltGr" 327 | }, 328 | { 329 | "0": "Break", 330 | "1": "Break", 331 | "2": "Break" 332 | }, 333 | { 334 | "0": "Find" 335 | }, 336 | { 337 | "0": "Up" 338 | }, 339 | { 340 | "0": "Prior" 341 | }, 342 | { 343 | "0": "Left" 344 | }, 345 | { 346 | "0": "Right" 347 | }, 348 | { 349 | "0": "Select" 350 | }, 351 | { 352 | "0": "Down" 353 | }, 354 | { 355 | "0": "Next" 356 | }, 357 | { 358 | "0": "Insert" 359 | }, 360 | { 361 | "0": "Remove" 362 | }, 363 | { 364 | "0": "Macro", 365 | "1": "Macro", 366 | "2": "Macro" 367 | }, 368 | { 369 | "0": "F13", 370 | "1": "F13", 371 | "2": "F13" 372 | }, 373 | { 374 | "0": "F14", 375 | "1": "F14", 376 | "2": "F14" 377 | }, 378 | { 379 | "0": "Help", 380 | "1": "Help", 381 | "2": "Help" 382 | }, 383 | { 384 | "0": "Do", 385 | "1": "Do", 386 | "2": "Do" 387 | }, 388 | { 389 | "0": "F17", 390 | "1": "F17", 391 | "2": "F17" 392 | }, 393 | { 394 | "0": "KP_MinPlus", 395 | "1": "KP_MinPlus", 396 | "2": "KP_MinPlus" 397 | }, 398 | { 399 | "0": "Pause" 400 | }, 401 | null, 402 | { 403 | "0": "KP_Period" 404 | }, 405 | null, 406 | null, 407 | null, 408 | { 409 | "0": "Alt" 410 | }, 411 | { 412 | "0": "Alt" 413 | } 414 | ] 415 | -------------------------------------------------------------------------------- /lib/fonts.js: -------------------------------------------------------------------------------- 1 | var Future = require('future') 2 | , path = require('path') 3 | , fs = require('fs'); 4 | 5 | var app_dir = path.dirname(require.main.filename) 6 | , fonts_dir = path.join(app_dir, 'public', 'fonts'); 7 | 8 | exports.list_fonts = function (filter) { 9 | var future = new Future; 10 | fs.readdir(fonts_dir, function (err, dir) { 11 | if (err) 12 | return future.deliver(err); 13 | if (filter) 14 | try { 15 | var re = new RegExp('^'+filter.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1").replace(/\\([*?])/g, '.$1')+'\..*$'); 16 | dir = dir.filter(re.test.bind(re)); 17 | } catch (e) { 18 | return future.deliver(e); 19 | } 20 | future.deliver(null, dir); 21 | }); 22 | return future; 23 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xserver", 3 | "version": "0.0.1", 4 | "description": "Javascript X11 Server", 5 | "main": "proxy.js", 6 | "dependencies": { 7 | "async": "*", 8 | "express": "*", 9 | "future": "*", 10 | "ip-address": "*", 11 | "morgan": "*", 12 | "react": "*", 13 | "react-tools": "*", 14 | "requirejs": "*", 15 | "source-map-support": "^0.3.2", 16 | "supervisor": "*", 17 | "websocket": "*", 18 | "wolfy-eventemitter": "*" 19 | }, 20 | "devDependencies": { 21 | "babel": "^5.8.23", 22 | "babel-runtime": "^5.8.25", 23 | "babelify": "^6.1.3", 24 | "browserify": "^11.2.0", 25 | "factor-bundle": "^2.5.0", 26 | "gulp": "^3.9.0", 27 | "gulp-insert": "^0.5.0", 28 | "gulp-rename": "^1.2.2", 29 | "gulp-sourcemaps": "^1.5.2", 30 | "gulp-util": "^3.0.6", 31 | "gulp-watch": "^4.2.4", 32 | "vinyl-buffer": "^1.0.0", 33 | "vinyl-source-stream": "^1.1.0", 34 | "vinyl-transform": "^1.0.0" 35 | }, 36 | "scripts": { 37 | "test": "echo \"Error: no test specified\" && exit 1", 38 | "start": "supervisor -w proxy.js proxy", 39 | "cloc": "cloc --exclude-dir=node_modules,public/lib,fonts,public/fonts,public/xcb ." 40 | }, 41 | "repository": { 42 | "type": "git", 43 | "url": "git://github.com/GothAck/javascript-x-server.git" 44 | }, 45 | "keywords": [ 46 | "x11" 47 | ], 48 | "author": "Greg Miell ", 49 | "license": "BSD-2-Clause", 50 | "gitHead": "b9e6f6729a00444ea0c4988bc33c51bee05f2041", 51 | "readmeFilename": "readme.md" 52 | } 53 | -------------------------------------------------------------------------------- /proxy.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , morgan = require('morgan') 3 | , http = require('http') 4 | , net = require('net') 5 | , WSS = require('websocket').server 6 | , child_process = require('child_process') 7 | , util = require('util') 8 | , EventEmitter = require('events').EventEmitter 9 | , ip = require('ip-address') 10 | , lib_fonts = require('./lib/fonts'); 11 | 12 | var app = exports.app = express() 13 | // .use(morgan('combined')) 14 | .use(express.static(__dirname + '/public')); 15 | 16 | app.get('/fonts', function (req, res) { 17 | lib_fonts.list_fonts(req.query.filter) 18 | .when(function (err, fonts) { 19 | if (err) 20 | return res.send(500, err); 21 | res.send(fonts); 22 | }); 23 | }) 24 | 25 | var server = exports.server = http.createServer(app) 26 | 27 | if (require.main === module) 28 | server.listen(3000); 29 | 30 | var screens = [41]; 31 | 32 | var wss = new WSS({ 33 | httpServer: server 34 | , autoAcceptConnections: false 35 | }); 36 | 37 | function X11Proxy (screen, connection, window_manager) { 38 | var self = this; 39 | this.screen = screen; 40 | this.connection = connection; 41 | 42 | this.id = ['family', 'address', 'port'] 43 | .map(function (k) { return connection.socket._peername[k] }) 44 | .join(':'); 45 | this.client_sockets = {}; 46 | this.ping = { 47 | counter: 0 48 | , interval: null 49 | , timeout: null 50 | , start: null 51 | , results: [] 52 | , average: 0 53 | }; 54 | this.processes = []; 55 | this.connection.on('message', this.data.bind(this)); 56 | this.connection.on('close', this.close.bind(this)); 57 | this.connection.sendUTF('SCR ' + this.screen); 58 | this.server_socket = net.createServer(this.newClient.bind(this)).listen(6000 + this.screen); 59 | if (window_manager) 60 | setTimeout(function () { 61 | self.spawnProcess(window_manager, []); //['-fn', 'cursor']); 62 | }, 750); 63 | } 64 | util.inherits(X11Proxy, EventEmitter); 65 | X11Proxy.prototype.newClient = function (socket) { 66 | var self = this 67 | , idBuf = new Buffer(19) 68 | , idStr; 69 | idBuf.fill(0); 70 | idBuf.writeUInt16BE(socket.remotePort, 16); 71 | if (net.isIPv4(socket.remoteAddress)) { 72 | idBuf.writeUInt8(4, 18); 73 | idBuf = socket.remoteAddress.split('.') 74 | .reduce( 75 | function (o, v, i) { 76 | o.writeUInt8(v, i); 77 | return o; 78 | } 79 | , idBuf 80 | ); 81 | } else { 82 | idBuf.writeUInt8(6, 18); 83 | idBuf = (new ip.v6.Address(socket.remoteAddress)).parsedAddress 84 | .reduce( 85 | function (o, v, i) { 86 | o.writeUInt16BE(v, i * 2); 87 | return o; 88 | } 89 | , idBuf 90 | ); 91 | } 92 | idStr = idBuf.toString('hex'); 93 | 94 | this.client_sockets[idStr] = socket; 95 | this.connection.sendUTF('NEW ' + idStr); 96 | socket.on('close', function () { 97 | self.connection.sendUTF('END ' + idStr); 98 | delete self.client_sockets[idStr]; 99 | }); 100 | socket.on('data', function (data) { 101 | var buffer = new Buffer(data.length + 19); 102 | idBuf.copy(buffer, 0) 103 | data.copy(buffer, 19); 104 | self.connection.sendBytes(buffer); 105 | }); 106 | } 107 | X11Proxy.prototype.data = function (message) { 108 | if (message.type === 'utf8') { 109 | var data = (message.utf8Data || '').split(' '); 110 | switch (data[0]) { 111 | case 'PONG': 112 | if (data[1] == this.ping.counter - 1) { 113 | this.ping.results.unshift(Date.now() - this.ping.start); 114 | this.ping.results.splice(10, 10); 115 | this.ping.average = this.ping.results.reduce(function (o, v) { return o + v }) / this.ping.results.length 116 | clearTimeout(this.ping.timeout); 117 | this.ping.timeout = null; 118 | } else { 119 | console.log('Out of order ping', data[1], this.ping.counter - 1); 120 | } 121 | break; 122 | } 123 | } else { 124 | var data = message.binaryData 125 | , length = data.readUInt8(0) 126 | , id = data.toString('ascii', 1, length + 1); 127 | if (this.client_sockets[id] && this.client_sockets[id].writable) { 128 | this.client_sockets[id].write(data.slice(length + 1)); 129 | } else { 130 | console.error('Socket closed already', length, id); 131 | this.connection.sendUTF('END ' + id); 132 | } 133 | } 134 | } 135 | X11Proxy.prototype.close = function () { 136 | console.log('closed'); 137 | clearTimeout(this.ping.timeout); 138 | clearInterval(this.ping.interval); 139 | this.processes.forEach(function (process) { 140 | process.kill(); 141 | }); 142 | Object.keys(this.client_sockets).forEach(function (id) { 143 | this.client_sockets[id].end(); 144 | }, this); 145 | try { 146 | this.server_socket.close(); 147 | } catch (e) {} 148 | screens.push(this.screen); 149 | } 150 | X11Proxy.prototype.spawnProcess = function (command, arguments) { 151 | var env = {} 152 | , prefix 153 | , child; 154 | Object.keys(process.env) 155 | .forEach(function (k) { 156 | env[k] = process.env[k]; 157 | }); 158 | env.DISPLAY = 'localhost:' + this.screen; 159 | this.processes.push( 160 | child = child_process.spawn(command, arguments || [], { env: env }) 161 | ); 162 | prefix = [this.id, child.pid, command, ': \t'].join(' '); 163 | function handleOutput (data) { 164 | console.log(prefix, data.toString().replace('\n', '\n' + prefix + ' ')); 165 | } 166 | child.stdout.on('data', handleOutput); 167 | child.stderr.on('data', handleOutput); 168 | } 169 | 170 | wss.on('request', function (req) { 171 | var screen = screens.shift(); 172 | 173 | if (!(req.origin && screen)) { 174 | console.log('reject'); 175 | return req.reject(); 176 | } 177 | console.log('New client'); 178 | var proxy = new X11Proxy(screen, req.accept('x11-proxy', req.origin), 'xeyes'); //, 'blackbox' Don't load blackbox by default 179 | /* 180 | ping_interval = setInterval(function () { 181 | var counter = ping_counter ++; 182 | con.sendUTF('PING ' + counter); 183 | ping_start = Date.now(); 184 | ping_timeout = setTimeout(function () { 185 | console.log('Ping timeout', counter); 186 | ping_timeout = null; 187 | }, 500); 188 | }, 5000); 189 | */ 190 | }); 191 | 192 | -------------------------------------------------------------------------------- /public/audio/bell.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/public/audio/bell.mp3 -------------------------------------------------------------------------------- /public/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/public/check.png -------------------------------------------------------------------------------- /public/fonts: -------------------------------------------------------------------------------- 1 | ../fonts/out -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | X Session 5 | 6 | 7 | 42 | 43 | 44 | 45 |

46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 56 | 77 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /public/require.config.js: -------------------------------------------------------------------------------- 1 | requirejs.config({ 2 | baseUrl: 'modules', 3 | paths: { 4 | site: '../site', 5 | lib: '../lib' 6 | }, 7 | shim: { 8 | 'lib/jsbn-combined': { 9 | exports: 'BigInteger' 10 | } 11 | , 'lib/sprintf': { 12 | exports: 'sprintf' 13 | } 14 | , 'lib/ipv6': { 15 | deps: ['lib/jsbn-combined', 'lib/sprintf'] 16 | , exports: 'v6' 17 | } 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /public/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # JavaScript X Server 2 | 3 | ## Concept 4 | 5 | ### Now 6 | 7 | This is currently very much a work in progress, with me learning about how an X11 server operates and it's underlying protocol. 8 | A subset of the protocol is now working within Chromium with output to canvas and div elements. 9 | Both xlogo and xeyes are 100% functional, with work currently being done to support xfn (with bitmap fonts)! 10 | 11 | ### Future 12 | 13 | The project may eventually have both server and client side X processing, 14 | allowing for optimisation of the X protocol and compression of Pixmaps before transferring to the client allowing 15 | for lightweight remote desktop connections in a web browser. 16 | 17 | It'd also be great to experiement with GLX and WebGL to see if there is enough crossover to allow 3d rendering via the browser! 18 | 19 | ## Loose Requirements 20 | * A basic window manager or application (we're talking blackbox wm, xlogo, xeyes, xfd, more complex apps are more likely to hit bugs / unknown features / unimplemented opcodes). 21 | * Developed on Mac, but should also work on Ilnux boxes 22 | 23 | ## Getting started 24 | 1. `git clone https://github.com/GothAck/javascript-x-server.git xserver; cd xserver` 25 | 2. `npm install` 26 | 3. Edit proxy.js: 27 | 28 | Change line: 29 | 30 | ` var proxy = new X11Proxy(screen, req.accept('x11-proxy', req.origin));` 31 | 32 | To contain the desired wm/application: 33 | 34 | ` var proxy = new X11Proxy(screen, req.accept('x11-proxy', req.origin), 'xeyes');` 35 | 4. In one terminal: `grunt; grunt watch` (you can just run `grunt`) 36 | 5. In another: `npm start` 37 | 6. Open http://localhost:3000 in a decent browser (currently only Chrome is tested working) 38 | -------------------------------------------------------------------------------- /rgb.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var rgbs = fs.readFileSync('rgb.txt', 'ascii'); 4 | rgbs = rgbs.split('\n').reduce(function (obj, line) { 5 | if (/^[!#]/.test(line)) 6 | return obj; 7 | var vals = line.match(/^(\d+)\s+(\d+)\s+(\d+)\s+([\w\s]+)$/); 8 | if (! vals) 9 | return obj; 10 | obj[vals[4]] = ((vals[1] ^ 0) << 16) | ((vals[2] ^ 0) << 8) | (vals[3] ^ 0); 11 | return obj; 12 | }, {}); 13 | 14 | fs.writeFileSync( 15 | 'public/rgb_colors.js' 16 | , '(function (module) {\n' 17 | + ' module.exports.rgb_colors = ' + JSON.stringify(rgbs, null, 2) 18 | + '})({ exports: window });' 19 | ); 20 | 21 | console.log(rgbs); 22 | -------------------------------------------------------------------------------- /src/common.js: -------------------------------------------------------------------------------- 1 | var useCaptureStackTrace = !!Error.captureStackStace; 2 | 3 | import type EndianBuffer from './endianbuffer'; 4 | 5 | export class MustImplementError extends Error { 6 | constructor(cls, method) { 7 | super(`Subclasses of ${cls} must implement ${method}`); 8 | useCaptureStackTrace && Error.captureStackStace(this, MustImplementError); 9 | } 10 | } 11 | 12 | export class MaskedField extends Map { 13 | static fields: Map; 14 | static default_type: string = 'UInt32'; 15 | static type_lengths: Map = new Map([ 16 | ['UInt8', 1], 17 | ['UInt16', 2], 18 | ['UInt32', 4], 19 | ['Int8', 1], 20 | ['Int16', 2], 21 | ['Int32', 4], 22 | ]); 23 | 24 | constructor(vmask: Number, vdata?: EndianBuffer) { 25 | super(); 26 | if (vmask === 0) { 27 | return; 28 | } 29 | var fields = this.constructor.fields; 30 | var default_type = this.constructor.default_type; 31 | var type_lengths = this.constructor.type_lengths; 32 | var i = 0; 33 | var offset = 0; 34 | for (let [k, v] of fields) { 35 | if (vmask & (2 ** i)) { 36 | let type = v || default_type; 37 | let value = vdata['readUInt32'](offset); 38 | this.set(k, value); 39 | offset += 4; // type_lengths.get(type); 40 | } 41 | i++; 42 | } 43 | } 44 | 45 | writeBuffer(buffer: EndianBuffer, offset?: number) { 46 | var fields = this.constructor.fields; 47 | var default_type = this.constructor.default_type; 48 | var type_lengths = this.constructor.type_lengths; 49 | var vmask = 0; 50 | for (let [k, v] of this.map) { 51 | let type = fields.get(k) || default_type; 52 | buffer['write' + type](v, offset); 53 | offset += type_lengths.get(type); 54 | } 55 | } 56 | 57 | toMap() { 58 | return new Map(this); 59 | } 60 | 61 | toObject() { 62 | var o = {}; 63 | for (let [k, v] of this) { 64 | o[k] = v; 65 | } 66 | return o; 67 | } 68 | 69 | static fromMap(map) { 70 | var mf = new this(0); 71 | for (let [k, _] of this.fields) { 72 | if (map.has(k)) { 73 | mf.set(k, map.get(k)); 74 | } 75 | } 76 | return mf; 77 | } 78 | 79 | static fromObject(o) { 80 | if (o instanceof Map) { 81 | return this.fromMap(o); 82 | } 83 | var mf = new this(0); 84 | if (o) { 85 | for (let [k, _] of this.fields) { 86 | if (o.hasOwnProperty(k)) { 87 | mf.set(k, o[k]); 88 | } 89 | } 90 | } 91 | return mf; 92 | } 93 | 94 | static getFieldNames(vmask: Number): Array { 95 | var names = []; 96 | var fields = this.fields; 97 | var i = 0; 98 | for (let [k, v] of fields) { 99 | if (vmask & Math.pow(2, i)) { 100 | names.push(k); 101 | } 102 | } 103 | return names; 104 | } 105 | } 106 | 107 | export class GCVField extends MaskedField { 108 | static fields = new Map([ 109 | 'function', 'plane_mask', 'foreground' , 'background', 'line_width', 110 | 'line_style', 'cap_style', 'join_style', 'fill_style', 'fill_rule', 111 | 'tile', 'stipple', 'tile_stipple_x_origin', 'tile_stipple_y_origin', 'font', 112 | 'subwindow_mode', 'graphics_exposures', 'clip_x_origin', 'clip_y_origin', 113 | 'clip_mask', 'dash_offset', 'gc_dashes', 'arc_mode' 114 | ].map((v) => [v])); 115 | } 116 | 117 | export class WinVField extends MaskedField { 118 | static fields = new Map([ 119 | ['background_pixmap', 'UInt32'], 120 | ['background_pixel', 'UInt32'], 121 | ['border_pixmap', 'UInt32'], 122 | ['border_pixel', 'UInt32'], 123 | ['bit_gravity', 'UInt8' ], 124 | ['win_gravity', 'UInt8' ], 125 | ['backing_store', 'UInt8' ], 126 | ['backing_planes', 'UInt32'], 127 | ['backing_pixel', 'UInt32'], 128 | ['override_redirect', 'UInt8' ], 129 | ['save_under', 'UInt8' ], 130 | ['event_mask', 'UInt32'], 131 | ['do_not_propagate_mask', 'UInt32'], 132 | ['colormap', 'UInt32'], 133 | ['cursor', 'UInt32'], 134 | ]); 135 | } 136 | 137 | export class WinConfigureField extends MaskedField { 138 | static fields = new Map( 139 | ['x', 'y', 'width', 'height', 'border_width', 'sibling', 'stack_mode'] 140 | .map((v) => [v]) 141 | ); 142 | } 143 | 144 | export function* yieldAll(...generators) { 145 | for (var gen of generators) { 146 | yield* gen; 147 | } 148 | } 149 | 150 | export class ArrayMap extends Map> { 151 | set(key: K, value: Array): void { 152 | throw new Error('set is not callable on ArrayMap') 153 | } 154 | 155 | addTo(key: K, value: V): self { 156 | if (!this.has(key)) { 157 | super.set(key, []); 158 | } 159 | this.get(key).push(value); 160 | return this; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/elements.js: -------------------------------------------------------------------------------- 1 | import * as x_types from "./x_types"; 2 | 3 | var x11_dom_event_map = new Map(( 4 | for (p of x_types.events.map.values()) 5 | if (p.dom_event) 6 | [p.dom_event, p])); 7 | 8 | function _HTMLDivElement() {} 9 | _HTMLDivElement.prototype = HTMLDivElement.prototype; 10 | 11 | export class XDrawableElement extends _HTMLDivElement { 12 | createElement(name, props, style) { 13 | var elem = document.createElement(name); 14 | if (props) { 15 | for (let k in props) { 16 | elem[k] = props[k]; 17 | } 18 | } 19 | if (style) { 20 | for (let k in style) { 21 | elem.style[k] = style[k]; 22 | } 23 | } 24 | return elem; 25 | } 26 | createdCallback() { 27 | var shadow = this.createShadowRoot(); 28 | this.canvas = this.createElement('canvas'); 29 | shadow.appendChild(this.canvas); 30 | shadow.appendChild( 31 | document.importNode( 32 | document.getElementById('x-drawable-style').content, true)); 33 | } 34 | attributeChangedCallback(name, oldVal, newVal) { 35 | switch (name) { 36 | case 'width': 37 | this.canvas.width = newVal; 38 | this.style.width = newVal + 'px'; 39 | break; 40 | case 'height': 41 | this.canvas.height = newVal; 42 | this.style.height = newVal + 'px'; 43 | break; 44 | } 45 | } 46 | } 47 | 48 | var mouse_buttons = [1,3,2]; 49 | var events = [ 50 | 'blur', 51 | 'focus', 52 | 'focusin', 53 | 'focusout', 54 | 'load', 55 | 'resize', 56 | 'scroll', 57 | 'unload', 58 | 'click', 59 | 'dblclick', 60 | 'mousedown', 61 | 'mouseup', 62 | 'mousemove', 63 | 'mouseover', 64 | 'mouseout', 65 | 'mouseenter', 66 | 'mouseleave', 67 | 'change', 68 | 'select', 69 | 'submit', 70 | 'keydown', 71 | 'keypress', 72 | 'keyup', 73 | 'error' 74 | ]; 75 | 76 | export class XWindowElement extends XDrawableElement { 77 | getCustomEventData(dom_event) { 78 | var server = this.xob.owner.server; 79 | var keybutmask = ( 80 | (server && server.buttons || 0) | 81 | // ( 82 | // dom_event.type === 'mousedown' && 83 | // (1 << (mouse_buttons[dom_event.button] - 1)) 84 | // ) 85 | 0 86 | ) << 8; 87 | if (dom_event.type === 'mousedown' || dom_event.type === 'mouseup') { 88 | keybutmask |= 0x10; 89 | keybutmask &= ~((1 << (mouse_buttons[dom_event.button] - 1)) << 8); 90 | } 91 | // keybutmask |= dom_event.shiftKey && 1; 92 | // lock? = 2 93 | // keybutmask |= dom_event.ctrlKey && 4; 94 | 95 | return { 96 | x: dom_event.offsetX 97 | , y: dom_event.offsetY 98 | , root_x: dom_event.offsetX 99 | , root_y: dom_event.offsetY 100 | , button: mouse_buttons[dom_event.button] 101 | , keycode: dom_event.keyCode 102 | , keybutmask: keybutmask 103 | }; 104 | } 105 | browserEventCallback(event) { 106 | var X11Event = x_types.events.dom_event_to_x11_map.get(event.type); 107 | if (X11Event) { 108 | var src = this; 109 | var window = this.xob; 110 | if (event.type === 'mouseover') { 111 | this.classList.add('hover'); 112 | } 113 | if (event.type === 'mouseout') { 114 | this.classList.remove('hover'); 115 | } 116 | var x11_event = new X11Event(window, this.getCustomEventData(event)) 117 | window.triggerEvent(x11_event); 118 | event.stopPropagation(); 119 | } 120 | } 121 | createdCallback() { 122 | super.createdCallback(); 123 | if (!this.void_events) { 124 | for (let event of events) { 125 | this.addEventListener(event, (e) => this.browserEventCallback(e)); 126 | } 127 | this.addEventListener('SendMask', (e) => { 128 | var xob = this.xob; 129 | var event_mask = e.detail.event_mask; 130 | var x_event = e.detail.event; 131 | if ((xob.event_mask && event_mask) || ! event_mask) { 132 | x_event.event_window = xob; 133 | xob.onEvent('SendEvent', x_event); 134 | e.stopPropagation(); 135 | } 136 | }); 137 | } 138 | this.style.display = 'none'; 139 | this.addEventListener('contextmenu', (e) => { 140 | e.stopPropagation(); 141 | e.preventDefault(); 142 | }); 143 | var shadow = this.createShadowRoot(); 144 | var relative = this.createElement('div', {className: 'relative'}) 145 | this.container = this.createElement('div', {className: 'container'}); 146 | 147 | shadow.appendChild( 148 | document.importNode( 149 | document.getElementById('x-window-style').content, true)); 150 | 151 | shadow.appendChild(this.container); 152 | this.container.appendChild(relative); 153 | relative.appendChild(this.createElement('shadow')); 154 | relative.appendChild(this.createElement('content')); 155 | } 156 | attributeChangedCallback(name, oldVal, newVal) { 157 | super.attributeChangedCallback(name, oldVal, newVal); 158 | switch (name) { 159 | case 'width': 160 | this.container.style.width = newVal + 'px'; 161 | break; 162 | case 'height': 163 | this.container.style.height = newVal + 'px'; 164 | break; 165 | case 'mapped': 166 | if (newVal) { 167 | this.style.display = 'block'; 168 | } else { 169 | this.style.display = 'none'; 170 | } 171 | } 172 | } 173 | collidesWith(...elems) { 174 | var res = []; 175 | for (let elem of elems) { 176 | if (elem.tagName !== this.tagName) { 177 | throw new Error('x-window.collidesWith must take another x-window elem'); 178 | } 179 | var t_br = this.getBoundingClientRect(); 180 | var e_br = elem.getBoundingClientRect(); 181 | if ( 182 | (t_br.left > e_br.right || e_br.left > t_br.right) || 183 | (t_br.top < e_br.bottom || e_br.top < t_br.bottom) 184 | ) { 185 | continue; 186 | } 187 | res.push(elem); 188 | } 189 | if (res.length) { 190 | return res; 191 | } 192 | return false; 193 | } 194 | *genPreviousSiblings() { 195 | var elem = this 196 | while (elem = elem.previousSibling) { 197 | yield elem; 198 | } 199 | } 200 | *genNextSiblings() { 201 | var elem = this 202 | while (elem = elem.nextSibling) { 203 | yield elem; 204 | } 205 | } 206 | *genSiblings() { 207 | yield* this.genNextSiblings(); 208 | yield* this.genPreviousSiblings(); 209 | } 210 | hasPrevSibling(...siblings) { 211 | for (let elem of this.genPreviousSiblings()) { 212 | if (siblings.includes(elem)) { 213 | return true; 214 | } 215 | } 216 | return false; 217 | } 218 | hasNextSibling(...siblings) { 219 | for (let elem of this.genNextSiblings()) { 220 | if (siblings.includes(elem)) { 221 | return true; 222 | } 223 | } 224 | return false; 225 | } 226 | prependChild(elem) { 227 | if (this.childNodes.length) { 228 | this.insertBefore(elem, this.childNodes[0]); 229 | } else { 230 | this.appendChild(elem); 231 | } 232 | } 233 | insertAfter(elem, after) { 234 | if (after.nextSibling) { 235 | this.insertBefore(elem, after.nextSibling); 236 | } else { 237 | this.appendChild(elem); 238 | } 239 | } 240 | } 241 | 242 | export class XScreenElement extends XWindowElement { 243 | createdCallback() { 244 | this.void_events = true; 245 | super.createdCallback(); 246 | this.addEventListener('mousedown', (e) => { 247 | this.xob.owner.buttons |= 1 << (mouse_buttons[e.button] - 1); 248 | }, true); 249 | this.addEventListener('mouseup', (e) => { 250 | this.xob.owner.buttons &= ~ (1 << (mouse_buttons[e.button] - 1)); 251 | }, true); 252 | this.addEventListener('mousemove', (e) => { 253 | this.xob.owner.mouseX = e.offsetX; 254 | this.xob.owner.mouseY = e.offsetY; 255 | }, true); 256 | for (let [name, XEvent] of x_types.events.x11_events_map) { 257 | if (XEvent.grab) { 258 | this.addEventListener( 259 | name, 260 | (event) => this.xob.owner.screenEvent(event, true), 261 | true); 262 | // this.addEventListener( 263 | // name, 264 | // (event) => this.xob.owner.screenEvent(event)); 265 | } 266 | } 267 | this.setAttribute('mapped', true); 268 | 269 | } 270 | } 271 | 272 | export function register() { 273 | document.registerElement( 274 | 'x-drawable', {prototype: XDrawableElement.prototype}); 275 | document.registerElement( 276 | 'x-window', {prototype: XWindowElement.prototype}); 277 | document.registerElement( 278 | 'x-screen', {prototype: XScreenElement.prototype}); 279 | } 280 | -------------------------------------------------------------------------------- /src/endianbuffer.js: -------------------------------------------------------------------------------- 1 | export default class EndianBuffer { 2 | constructor(buf) { 3 | this.endian = false; 4 | if (typeof buf === 'number') { 5 | this.buffer = new ArrayBuffer(buf); 6 | } else if (buf instanceof ArrayBuffer) { 7 | this.buffer = buf; 8 | } else if (buf instanceof EndianBuffer) { 9 | this.buffer = new ArrayBuffer(buf.length); 10 | buf.copy(this); 11 | } else if (typeof buf === 'string') { 12 | this.buffer = new ArrayBuffer(buf.length / 2); 13 | } 14 | this.dataview = new DataView(this.buffer, 0); 15 | if (typeof buf === 'string') 16 | for (var i = 0; i < buf.length; i += 2) 17 | this.writeUInt8(parseInt(buf.slice(i, i + 2), 16), i / 2); 18 | } 19 | 20 | get length() { 21 | return this.buffer.byteLength; 22 | } 23 | 24 | copy(buffer, t_start, s_start, s_end) { 25 | t_start = t_start || 0; 26 | s_start = s_start || 0; 27 | s_end = s_end || Math.min(this.length, buffer.length); 28 | var length = s_end - s_start 29 | , src_arr = new Uint8Array(this.buffer, s_start, length) 30 | , dst_arr = new Uint8Array(buffer.buffer, t_start, length) 31 | dst_arr.set(src_arr); 32 | return length; 33 | } 34 | 35 | slice(start, end) { 36 | start = start || 0; 37 | end = end || this.length; 38 | var newb = new (this.constructor)(this.buffer.slice(start, end)); 39 | newb.endian = this.endian; 40 | return newb; 41 | } 42 | 43 | toString(type, from, to) { 44 | switch (type) { 45 | case 'ascii': 46 | if ((!from) || (!to) || (to - from > 2000)) 47 | console.log('break'); 48 | var arr = new Uint8Array(this.buffer.slice(from, to)) 49 | , str = ''; 50 | for (var i = 0; i < arr.length; i++) 51 | str += String.fromCharCode(arr[i]); 52 | return str; 53 | case '2charb': 54 | if (((to - from) ^ 2) !== 0) 55 | throw new Error('String length should be multiple of two!'); 56 | var arr = new Uint8Array(this.buffer.slice(from, to)) 57 | , str = ''; 58 | for (var i = 0; i < arr.length; i += 2) 59 | str += String.fromCharCode( (arr[i * 2] << 8) + arr[(i * 2) + 1] ); 60 | return str; 61 | case 'hex': 62 | var arr = new Uint8Array(this.buffer.slice(from, to)) 63 | , str = ''; 64 | for (var i = 0; i < arr.length; i++) 65 | str += (arr[i] < 16 ? '0' : '') + arr[i].toString(16); 66 | return str; 67 | default: 68 | return null; 69 | } 70 | } 71 | 72 | write(string, offset, length, encoding) { 73 | offset = offset || 0; 74 | if (length === null) 75 | length = Infinity; 76 | length = Math.min(string.length, (this.buffer.byteLength - offset), length); 77 | encoding = encoding || 'ascii'; 78 | if (encoding !== 'ascii') 79 | throw new Error('Only ascii implemented'); 80 | var arr = new Uint8Array(this.buffer, offset, length); 81 | for (var i = 0; i < length; i ++) 82 | arr[i] = string.charCodeAt(i); 83 | return length; 84 | } 85 | 86 | fill(value, start, end) { 87 | value = value || 0; 88 | start = start || 0; 89 | end = end || 0; 90 | var arr = new Uint8Array(this.buffer, start, end - start); 91 | for (var i = 0; i < arr.length; i++) 92 | arr[i] = value; 93 | } 94 | 95 | static ensure(obj_arr) { 96 | var Constructor = this; 97 | if (Array.isArray(obj_arr)) { 98 | obj_arr.forEach(function (v, i, a) { 99 | if (v instanceof ArrayBuffer) 100 | a[i] = new Constructor(v); 101 | }); 102 | } else { 103 | var a = obj_arr; 104 | Object.keys(obj_arr).forEach(function (i) { 105 | var v = obj_arr[i]; 106 | if (v instanceof ArrayBuffer) 107 | a[i] = new Constructor(v); 108 | }); 109 | } 110 | } 111 | } 112 | 113 | ;(function (EndianBuffer) { 114 | var keys = ['Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Float32', 'Float64']; 115 | keys.forEach(function (key) { 116 | EndianBuffer.prototype['read' + key.replace('int', 'Int')] = function (offset) { 117 | return this.dataview['get' + key](offset, this.endian) 118 | } 119 | EndianBuffer.prototype['write' + key.replace('int', 'Int')] = function (value, offset) { 120 | return this.dataview['set' + key](offset, value, this.endian) 121 | } 122 | }); 123 | })(EndianBuffer); 124 | 125 | 126 | export class CursorBuffer extends EndianBuffer { 127 | static lengths = new Map([ 128 | ['Int8', 1], ['Uint8', 1], 129 | ['Int16', 2], ['Uint16', 2], 130 | ['Int32', 4], ['Uint32', 4], 131 | ['Float32', 4], ['Float64', 8], 132 | ]); 133 | 134 | constructor(buf) { 135 | super(buf); 136 | this.cursor = 0; 137 | } 138 | 139 | moveCursor(by) { 140 | this.cursor += by; 141 | } 142 | } 143 | 144 | ;(function (CursorBuffer) { 145 | var keys = ['Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Float32', 'Float64']; 146 | CursorBuffer.lengths.forEach(function (key_length, key) { 147 | CursorBuffer.prototype['read' + key.replace('int', 'Int')] = function (offset) { 148 | var val = this.dataview['get' + key](this.cursor, this.endian); 149 | this.moveCursor(key_length); 150 | return val; 151 | } 152 | CursorBuffer.prototype['write' + key.replace('int', 'Int')] = function (value, offset) { 153 | this.dataview['set' + key](this.cursor, value, this.endian); 154 | this.moveCursor(key_length); 155 | } 156 | }); 157 | })(CursorBuffer); 158 | 159 | class Enum extends Set { 160 | static _values: Map = new Map(); 161 | _decoded: boolean = false; 162 | 163 | constructor(values?: ?Iterable) { 164 | super(); 165 | if (values) { 166 | for (val of values) { 167 | this.add(val); 168 | } 169 | } 170 | } 171 | 172 | getFirst(): ?string { 173 | var out = null; 174 | for (let v of this) { 175 | out = v; 176 | break; 177 | } 178 | return out; 179 | } 180 | 181 | decode(value: number): self { 182 | if (this._values.has(value)) { 183 | this.add(this._values.get(value)); 184 | this._decoded = true; 185 | } 186 | return this; 187 | } 188 | 189 | encode(): ?number { 190 | // TODO: Performance, reverse the hash once? 191 | let out = null; 192 | if (this.size === 1 && this._values.size) { 193 | let first = this.getFirst(); 194 | if (first === null) { 195 | for (let [value, name] of this._values) { 196 | if (name === first) { 197 | out = value; 198 | break; 199 | } 200 | } 201 | } 202 | } 203 | return out; 204 | } 205 | } 206 | 207 | export class ValueEnum extends Enum {} 208 | 209 | export class BitEnum extends Enum { 210 | static _bits: Map = new Map() 211 | 212 | decode(value: number): self { 213 | super.decode(value) 214 | if (!this._decoded) { 215 | for (let [bit, bit_value] of this._bits) { 216 | if (value & Math.pow(bit, 2)) { 217 | this.add(bit_value); 218 | } 219 | } 220 | } 221 | return this; 222 | } 223 | 224 | encode(): ?number { 225 | let out = super.encode(); 226 | if (out !== null) { 227 | return out; 228 | } 229 | out = 0; 230 | for (let [bit, bit_value] of this._bits) { 231 | if (this.has(bit_value)) { 232 | out |= Math.pow(bit, 2); 233 | } 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/fs.js: -------------------------------------------------------------------------------- 1 | export function readFile(filename, encoding, callback) { 2 | if (typeof encoding === 'function') { 3 | callback = encoding; 4 | encoding = null; 5 | } 6 | encoding = encoding || 'raw'; 7 | 8 | var req = new XMLHttpRequest(); 9 | req.open('GET', window.location.protocol + '//' + window.location.host + '/' + filename, true); 10 | if (encoding === 'raw') 11 | req.responseType = 'arraybuffer'; 12 | req.onerror = function (event) { 13 | callback(req.status || event || 'Unknown Error'); 14 | callback = new Function; 15 | } 16 | req.onload = function (event) { 17 | callback((req.status === 200 ? null : req.status), req.response); 18 | } 19 | req.send(); 20 | } 21 | 22 | export function readFileAsync(filename, encoding) { 23 | return new Promise((res, rej) => { 24 | readFile(filename, encoding, (err, data) => { 25 | if (err) { 26 | return rej(err); 27 | } 28 | res(data); 29 | }) 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /src/keymap.js: -------------------------------------------------------------------------------- 1 | var keysyms = { 2 | 'Backspace' : 0xff08 3 | , 'Tab' : 0xff09 4 | , 'Linefeed' : 0xff0a 5 | , 'Clear' : 0xff0b 6 | , 'Return' : 0xff0d 7 | , 'Pause' : 0xff13 8 | , 'ScrollLock': 0xff14 9 | , 'SysReq' : 0xff15 10 | , 'Escape' : 0xff1b 11 | , 'Home' : 0xff50 12 | , 'Left' : 0xff51 13 | , 'Up' : 0xff52 14 | , 'Right' : 0xff53 15 | , 'Down' : 0xff54 16 | , 'PgUp' : 0xff55 17 | , 'PgDn' : 0xff56 18 | }; 19 | 20 | function charKeySym (char) { 21 | if (char.charCodeAt(0) < 0x100) 22 | return char.charCodeAt(0); 23 | return char.charCodeAt(0) + 0x01000100; 24 | } 25 | 26 | class KeyMap { 27 | constructor(map) { 28 | this.maxModifiers = 3; 29 | this._map = map; 30 | } 31 | get map() { return [null].concat(this._map) } 32 | getKeysym(string, shift) { 33 | switch (string.length) { 34 | case 1: 35 | return charKeySym(shift ? string.toUpperCase() : string.toLowerCase()); 36 | case 2: 37 | if (string.charAt(0) === '+') 38 | return charKeySym(shift ? string.charAt(1).toUpperCase() : string.charAt(1).toLowerCase()); 39 | default: 40 | if (string in keysyms) 41 | return keysyms[string]; 42 | return 0; 43 | } 44 | } 45 | find(name) { 46 | return this._map.reduce(function (array, value, index) { if (name.test ? name.test(value) : value === name) return array.concat([index + 1]); return array; }, []); 47 | } 48 | get(id) { 49 | return this._map[id - 1]; 50 | } 51 | clone() { 52 | return new KeyMap(this.map.slice()); 53 | } 54 | } 55 | 56 | export var getKeysym = KeyMap.prototype.getKeysym; 57 | 58 | export var maps = { 59 | gb: new KeyMap( 60 | [ 61 | null, 62 | { 63 | "0": "Escape" 64 | }, 65 | { 66 | "0": "one", 67 | "1": "exclam" 68 | }, 69 | { 70 | "0": "two", 71 | "1": "quotedbl" 72 | }, 73 | { 74 | "0": "three" 75 | }, 76 | { 77 | "0": "four", 78 | "1": "dollar" 79 | }, 80 | { 81 | "0": "five", 82 | "1": "percent" 83 | }, 84 | { 85 | "0": "six", 86 | "1": "asciicircum" 87 | }, 88 | { 89 | "0": "seven", 90 | "1": "ampersand", 91 | "2": "braceleft" 92 | }, 93 | { 94 | "0": "eight", 95 | "1": "asterisk", 96 | "2": "bracketleft" 97 | }, 98 | { 99 | "0": "nine", 100 | "1": "parenleft", 101 | "2": "bracketright" 102 | }, 103 | { 104 | "0": "zero", 105 | "1": "parenright", 106 | "2": "braceright" 107 | }, 108 | { 109 | "0": "minus", 110 | "1": "underscore", 111 | "2": "backslash" 112 | }, 113 | { 114 | "0": "equal" 115 | }, 116 | { 117 | "0": "Delete" 118 | }, 119 | { 120 | "0": "Tab" 121 | }, 122 | { 123 | "0": "+q", 124 | "1": "+Q", 125 | "2": "at" 126 | }, 127 | { 128 | "0": "+w", 129 | "1": "+W" 130 | }, 131 | { 132 | "0": "+e" 133 | }, 134 | { 135 | "0": "+r", 136 | "1": "+R" 137 | }, 138 | { 139 | "0": "+t", 140 | "1": "+T" 141 | }, 142 | { 143 | "0": "+y", 144 | "1": "+Y" 145 | }, 146 | { 147 | "0": "+u", 148 | "1": "+U" 149 | }, 150 | { 151 | "0": "+i", 152 | "1": "+I" 153 | }, 154 | { 155 | "0": "+o" 156 | }, 157 | { 158 | "0": "+p" 159 | }, 160 | { 161 | "0": "bracketleft" 162 | }, 163 | { 164 | "0": "bracketright" 165 | }, 166 | { 167 | "0": "Return" 168 | }, 169 | { 170 | "0": "Control" 171 | }, 172 | { 173 | "0": "+a" 174 | }, 175 | { 176 | "0": "+s", 177 | "1": "+S", 178 | "2": "+ssharp" 179 | }, 180 | { 181 | "0": "+d" 182 | }, 183 | { 184 | "0": "+f", 185 | "1": "+F" 186 | }, 187 | { 188 | "0": "+g", 189 | "1": "+G" 190 | }, 191 | { 192 | "0": "+h", 193 | "1": "+H" 194 | }, 195 | { 196 | "0": "+j", 197 | "1": "+J" 198 | }, 199 | { 200 | "0": "+k", 201 | "1": "+K" 202 | }, 203 | { 204 | "0": "+l", 205 | "1": "+L" 206 | }, 207 | { 208 | "0": "semicolon" 209 | }, 210 | { 211 | "0": "apostrophe" 212 | }, 213 | { 214 | "0": "grave" 215 | }, 216 | { 217 | "0": "Shift" 218 | }, 219 | { 220 | "0": "numbersign" 221 | }, 222 | { 223 | "0": "+z", 224 | "1": "+Z" 225 | }, 226 | { 227 | "0": "+x", 228 | "1": "+X" 229 | }, 230 | { 231 | "0": "+c", 232 | "1": "+C" 233 | }, 234 | { 235 | "0": "+v", 236 | "1": "+V" 237 | }, 238 | { 239 | "0": "+b", 240 | "1": "+B" 241 | }, 242 | { 243 | "0": "+n" 244 | }, 245 | { 246 | "0": "+m" 247 | }, 248 | { 249 | "0": "comma", 250 | "1": "less" 251 | }, 252 | { 253 | "0": "period", 254 | "1": "greater" 255 | }, 256 | { 257 | "0": "slash", 258 | "1": "question" 259 | }, 260 | { 261 | "0": "Shift" 262 | }, 263 | { 264 | "0": "KP_Multiply" 265 | }, 266 | { 267 | "0": "Alt" 268 | }, 269 | { 270 | "0": "space" 271 | }, 272 | { 273 | "0": "CtrlL_Lock" 274 | }, 275 | { 276 | "0": "F1" 277 | }, 278 | { 279 | "0": "F2" 280 | }, 281 | { 282 | "0": "F3" 283 | }, 284 | { 285 | "0": "F4" 286 | }, 287 | { 288 | "0": "F5" 289 | }, 290 | { 291 | "0": "F6" 292 | }, 293 | { 294 | "0": "F7" 295 | }, 296 | { 297 | "0": "F8" 298 | }, 299 | { 300 | "0": "F9" 301 | }, 302 | { 303 | "0": "F10" 304 | }, 305 | { 306 | "0": "Num_Lock" 307 | }, 308 | { 309 | "0": "Scroll_Lock" 310 | }, 311 | { 312 | "0": "KP_7" 313 | }, 314 | { 315 | "0": "KP_8" 316 | }, 317 | { 318 | "0": "KP_9" 319 | }, 320 | { 321 | "0": "KP_Subtract" 322 | }, 323 | { 324 | "0": "KP_4" 325 | }, 326 | { 327 | "0": "KP_5" 328 | }, 329 | { 330 | "0": "KP_6" 331 | }, 332 | { 333 | "0": "KP_Add" 334 | }, 335 | { 336 | "0": "KP_1" 337 | }, 338 | { 339 | "0": "KP_2" 340 | }, 341 | { 342 | "0": "KP_3" 343 | }, 344 | { 345 | "0": "KP_0" 346 | }, 347 | { 348 | "0": "KP_Period" 349 | }, 350 | { 351 | "0": "Last_Console", 352 | "1": "Last_Console", 353 | "2": "Last_Console" 354 | }, 355 | null, 356 | { 357 | "0": "backslash", 358 | "1": "bar", 359 | "2": "bar" 360 | }, 361 | { 362 | "0": "F11" 363 | }, 364 | { 365 | "0": "F12" 366 | }, 367 | null, 368 | null, 369 | null, 370 | null, 371 | null, 372 | null, 373 | null, 374 | { 375 | "0": "KP_Enter" 376 | }, 377 | { 378 | "0": "Control" 379 | }, 380 | { 381 | "0": "KP_Divide" 382 | }, 383 | null, 384 | { 385 | "0": "AltGr" 386 | }, 387 | { 388 | "0": "Break", 389 | "1": "Break", 390 | "2": "Break" 391 | }, 392 | { 393 | "0": "Find" 394 | }, 395 | { 396 | "0": "Up" 397 | }, 398 | { 399 | "0": "Prior" 400 | }, 401 | { 402 | "0": "Left" 403 | }, 404 | { 405 | "0": "Right" 406 | }, 407 | { 408 | "0": "Select" 409 | }, 410 | { 411 | "0": "Down" 412 | }, 413 | { 414 | "0": "Next" 415 | }, 416 | { 417 | "0": "Insert" 418 | }, 419 | { 420 | "0": "Remove" 421 | }, 422 | { 423 | "0": "Macro", 424 | "1": "Macro", 425 | "2": "Macro" 426 | }, 427 | { 428 | "0": "F13", 429 | "1": "F13", 430 | "2": "F13" 431 | }, 432 | { 433 | "0": "F14", 434 | "1": "F14", 435 | "2": "F14" 436 | }, 437 | { 438 | "0": "Help", 439 | "1": "Help", 440 | "2": "Help" 441 | }, 442 | { 443 | "0": "Do", 444 | "1": "Do", 445 | "2": "Do" 446 | }, 447 | { 448 | "0": "F17", 449 | "1": "F17", 450 | "2": "F17" 451 | }, 452 | { 453 | "0": "KP_MinPlus", 454 | "1": "KP_MinPlus", 455 | "2": "KP_MinPlus" 456 | }, 457 | { 458 | "0": "Pause" 459 | }, 460 | null, 461 | { 462 | "0": "KP_Period" 463 | }, 464 | null, 465 | null, 466 | null, 467 | { 468 | "0": "Alt" 469 | }, 470 | { 471 | "0": "Alt" 472 | } 473 | ] 474 | ) 475 | } 476 | -------------------------------------------------------------------------------- /src/site.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === "development") { 2 | // require('./stack_magick'); 3 | } 4 | 5 | import * as elements from './elements'; 6 | elements.register(); 7 | 8 | import EndianBuffer from './endianbuffer'; 9 | import XServer from './x_server'; 10 | import x_types from './x_types'; 11 | 12 | var h2 = document.getElementById('title'); 13 | 14 | var worker_comms = new Worker('worker_comms.js'); 15 | var server 16 | , connected = false; 17 | 18 | worker_comms.addEventListener('message', function (event) { 19 | switch (event.data.cmd) { 20 | case 'loaded': 21 | worker_comms.postMessage({ cmd: 'connect', address: window.location.host }); 22 | break; 23 | case 'open': 24 | connected = true; 25 | break; 26 | case 'close': 27 | connected = false; 28 | break; 29 | case 'new': 30 | server.newClient(event.data.id, event.data.host, event.data.port, event.data.host_type); 31 | h2.textContent = `${server.clients.size} clients`; 32 | break; 33 | case 'end': 34 | server.disconnect(event.data.id); 35 | h2.textContent = `${server.clients.size} clients`; 36 | break; 37 | case 'request': 38 | EndianBuffer.ensure(event.data.request); 39 | server.processRequest(event.data); 40 | break; 41 | case 'screen': 42 | server = window.server = new XServer(event.data.id, function (data, client, new_reply) { 43 | if (new_reply) 44 | return worker_comms.postMessage({ cmd: 'reply', id: client.id, data: data, state: client.state }); 45 | worker_comms.postMessage({ cmd: 'message', id: client.id, data:data, state: client.state }, [data]); 46 | }, document.getElementById('screen')); 47 | document.title = 'XSession :' + event.data.id; 48 | h2.textContent = `${server.clients.size} clients`; 49 | break; 50 | default: 51 | console.error('Unknown message', event.data); 52 | } 53 | }); 54 | 55 | -------------------------------------------------------------------------------- /src/stack_magick.js: -------------------------------------------------------------------------------- 1 | import {wrapCallSite} from 'source-map-support'; 2 | 3 | var prefix0 = /^0+/; 4 | 5 | function prepareStackTrace(error, stack) { 6 | error._xclient = null; 7 | return error + stack.map(function(frame) { 8 | var cs = wrapCallSite(frame, false); 9 | 10 | if (!(cs.isToplevel() || cs.isConstructor())) { 11 | var th = cs.getThis() || ''; 12 | if (th.constructor.name === 'XServerClient') { 13 | error._xclient = th; 14 | } 15 | var id = th.id || ''; 16 | if (id) { 17 | id = '<' + String(id).replace(prefix0, '') + '>'; 18 | } 19 | var typename = cs.getTypeName(); 20 | cs.getTypeName = () => typename + id; 21 | } 22 | return '\n at ' + cs; 23 | }).join(''); 24 | } 25 | 26 | Error.prepareStackTrace = prepareStackTrace; 27 | 28 | console.info('StackMagick™ loaded'); 29 | 30 | if (process.env.NODE_EXPERIMENTAL) { 31 | var _log = console.log; 32 | 33 | console.log = function log (...args) { 34 | var e = new Error; 35 | e.stack.toString(); 36 | if (e._xclient) { 37 | console::_log(e._xclient.id, ...args); 38 | } else { 39 | console::_log(...args); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/worker_comms.js: -------------------------------------------------------------------------------- 1 | import * as x_protocol from './x_protocol'; 2 | import * as x_protocol_new from './x_protocol_new'; 3 | import * as EndianBuffer from './endianbuffer'; 4 | 5 | var string_split = /(\w+)(\s\w+){0,1}$/ 6 | self.console = console; 7 | var buffer = null 8 | , clients = {} 9 | , server = null; 10 | 11 | var socket = null; 12 | self.addEventListener('message', function (event) { 13 | switch (event.data.cmd) { 14 | case 'connect': 15 | if (server) 16 | return postMessage({ cmd: 'error', message: 'Server exists!' }); 17 | var socket = new WebSocket('ws://' + event.data.address, 'x11-proxy'); 18 | socket.binaryType = 'arraybuffer'; 19 | server = new x_protocol.XProtocolServer(socket, function () { 20 | postMessage({ cmd: 'close' }) 21 | server = null; 22 | }); 23 | break; 24 | case 'disconnect': 25 | if (! server) 26 | return postMessage({ cmd: 'error', message: 'not connected' }) 27 | socket.close(); 28 | break; 29 | case 'message': 30 | if (! server) 31 | return postMessage({ cmd: 'error', message: 'not connected' }) 32 | if (! server.clients[event.data.id]) 33 | throw new Error('Invalid client! Disconnected?'); 34 | server.serverMessage(event.data); 35 | break; 36 | case 'reply': 37 | if (! server) 38 | return postMessage({ cmd: 'error', message: 'not connected' }) 39 | if (! server.clients[event.data.id]) 40 | throw new Error('Invalid client! Disconnected?'); 41 | server.serverReply(event.data); 42 | break; 43 | } 44 | }); 45 | self.postMessage({ cmd: 'loaded' }); 46 | -------------------------------------------------------------------------------- /src/worker_console.js: -------------------------------------------------------------------------------- 1 | if (typeof window === 'undefined') { 2 | window = this; 3 | } 4 | var counter = 0; 5 | if (window.document === undefined) { 6 | if (window.console === undefined) { 7 | console = ([ 8 | 'log' 9 | , 'warn' 10 | , 'error' 11 | , 'debug' 12 | , 'group' 13 | , 'groupEnd' 14 | , 'time' 15 | , 'timeEnd' 16 | ].reduce( 17 | function (o, func) { 18 | o[func] = function () { 19 | this._channel.port1.postMessage({ func: func, arguments: Array.prototype.slice.call(arguments), stack: (new Error).stack, count: counter++ }); 20 | }; 21 | return o; 22 | } 23 | , {} 24 | )); 25 | console._channel = new MessageChannel; 26 | postMessage('console', [console._channel.port2]); 27 | console.worker = true; 28 | } else { 29 | postMessage('consolenotrequired'); 30 | } 31 | } else { 32 | console.wrapWorker = function (worker) { 33 | worker._console_port = null; 34 | function messageHandler (event) { 35 | if (event.data === 'consolenotrequired') { 36 | worker.removeEventListener('message', messageHandler); 37 | event.stopImmediatePropagation(); 38 | return false; 39 | } 40 | if (event.data !== 'console') 41 | return true; 42 | worker._console_port = event.ports[0]; 43 | worker._console_port.onmessage = function (event) { 44 | var data = event.data 45 | , args = data.arguments; 46 | args.push('From worker ' + data.stack.split('\n')[2].trimLeft()); 47 | console[data.func].apply(console, args); 48 | } 49 | worker.removeEventListener('message', messageHandler); 50 | event.stopImmediatePropagation(); 51 | return false; 52 | } 53 | worker.addEventListener('message', messageHandler); 54 | worker.addEventListener('error', function (event) { 55 | console.error(event); 56 | }); 57 | return worker; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/x_protocol_new.js: -------------------------------------------------------------------------------- 1 | import { GCVField, WinVField, WinConfigureField } from './common'; 2 | import * as x_types from './x_types'; 3 | import EndianBuffer from './endianbuffer'; 4 | import { v6 } from 'ip-address'; 5 | 6 | import XTypeBuffer from './xtypebuffer'; 7 | 8 | export class XProtocolServer { 9 | constructor(socket, onClose) { 10 | console.log('new XProtocolServer') 11 | this.socket = socket; 12 | this.onClose = onClose; 13 | this.clients = {}; 14 | socket.addEventListener('message', (e) => this.socketMessage(e)); 15 | socket.addEventListener('close', (e) => this.socketClose(e)); 16 | socket.addEventListener('open', (e) => this.socketOpen(e)); 17 | } 18 | serverMessage(message) { 19 | var client = this.clients[message.id]; 20 | if (! client) 21 | postMessage({ cmd: 'error', message: 'not connected?' }); 22 | client.state = message.state; 23 | var data = new EndianBuffer(message.data) 24 | , buffer = new EndianBuffer(data.length + client.id.length + 1); 25 | buffer.writeUInt8(client.id.length, 0); 26 | buffer.write(client.id, 1, null, 'ascii'); 27 | data.copy(buffer, client.id.length + 1); 28 | this.socket.send(buffer.buffer); 29 | } 30 | serverReply(message) { 31 | try { 32 | var client = this.clients[message.id] 33 | , ReqObj = Request[Request.opcodes[message.data.opcode]] 34 | , Rep = ReqObj.Rep; 35 | var rep = new Rep(message.data, client) 36 | var data = rep.toBuffer() 37 | , buffer = new EndianBuffer(data.length + client.id.length + 1); 38 | buffer.writeUInt8(client.id.length, 0); 39 | buffer.write(client.id, 1, null, 'ascii'); 40 | data.copy(buffer, client.id.length + 1); 41 | this.socket.send(buffer.buffer); 42 | } catch (e) { 43 | console.error(e.toString(), e.stack); 44 | } 45 | } 46 | socketMessage(event) { 47 | if (event.data.constructor === String) { 48 | var data = event.data.split(' '); 49 | switch (data[0]) { 50 | case 'SCR': 51 | postMessage({ 52 | cmd: 'screen' 53 | , id: data[1] 54 | }); 55 | break 56 | case 'NEW': 57 | var client = this.clients[data[1]] = new XProtocolClient(data[1]); 58 | postMessage({ 59 | cmd: 'new' 60 | , id: data[1] 61 | , host: client.host 62 | , port: client.port 63 | , host_type: client.host_type 64 | }); 65 | break; 66 | case 'END': 67 | this.clients[data[1]].end(); 68 | delete this.clients[data[1]] 69 | break; 70 | case 'PING': 71 | this.socket.send('PONG'); 72 | break; 73 | default: 74 | console.error('Unknown message received', data.join(' ')); 75 | } 76 | } else { 77 | var data = new EndianBuffer(event.data) 78 | , idBuf = data.slice(0,19) 79 | , idStr = idBuf.toString('hex'); 80 | if (! this.clients[idStr]) 81 | throw new Error('Invalid client! Disconnected?'); 82 | this.clients[idStr].processData(data.slice(19)); 83 | } 84 | } 85 | socketClose(event) { 86 | postMessage({ cmd: 'close' }); 87 | this.socket = null; 88 | this.onClose && this.onClose(); 89 | } 90 | socketOpen(event) { 91 | postMessage({ cmd: 'open' }); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /types.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GothAck/javascript-x-server/23f5118db68ec99d19189a8fc57e5d5b8e7a9ed0/types.js -------------------------------------------------------------------------------- /x_types.js: -------------------------------------------------------------------------------- 1 | var Canvas = require('canvas'); 2 | 3 | Buffer.prototype.endian = null 4 | 5 | Object.keys(Buffer.prototype).filter(function (name) { return /(BE|LE)$/.test(name) }).map(function (name) { return name.slice(0, -2) }) 6 | .forEach(function (name) { 7 | Buffer.prototype[name] = function () { 8 | if (! this.endian) throw new Error('No endian'); 9 | return this[name + this.endian].apply(this, arguments); 10 | } 11 | }); 12 | 13 | var awb_open = 0; 14 | Array.prototype.writeBuffer = function (buffer, offset) { 15 | this.forEach(function (item) { 16 | offset = item.writeBuffer(buffer, offset); 17 | }); 18 | return offset; 19 | } 20 | 21 | Array.prototype.byteLength = function () { 22 | return this.reduce(function (p, c) { return p + c.length }, 0); 23 | } 24 | 25 | function Format (depth, bpp, scanline_pad) { 26 | this.depth = depth || 0; 27 | this.bpp = bpp || 0; 28 | this.scanline_pad = scanline_pad || 0; 29 | this.length = 8; 30 | } 31 | 32 | module.exports.Format = Format; 33 | 34 | Format.prototype.writeBuffer = function (buffer, offset) { 35 | buffer.writeUInt8(this.depth , offset); 36 | buffer.writeUInt8(this.bpp , offset + 1); 37 | buffer.writeUInt8(this.scanline_pad , offset + 2); 38 | // 5 unused 39 | return offset + this.length; 40 | } 41 | 42 | function VisualType (visualid, _class, bits_per_rgb, colormap_entries, red_mask, green_mask, blue_mask) { 43 | this.visualid = visualid || 0; 44 | this.class = _class || 0; 45 | this.bits_per_rgb = bits_per_rgb || 0; 46 | this.colormap_entries = colormap_entries || 0; 47 | this.red_mask = red_mask || 0; 48 | this.green_mask = green_mask || 0; 49 | this.blue_mask = blue_mask || 0; 50 | this.length = 24; 51 | } 52 | 53 | module.exports.VisualType = VisualType; 54 | 55 | VisualType.prototype.writeBuffer = function (buffer, offset) { 56 | buffer.writeUInt32(this.visualid, offset); // 4 57 | buffer.writeUInt8(this.class, offset + 4); // 1 58 | buffer.writeUInt8(this.bits_per_rgb, offset + 5); // 1 59 | buffer.writeUInt16(this.colormap_entries, offset + 6); // 2 60 | buffer.writeUInt32(this.red_mask, offset + 8); // 4 61 | buffer.writeUInt32(this.green_mask, offset + 12); // 4 62 | buffer.writeUInt32(this.blue_mask, offset + 16); // 4 63 | // 4 unused 64 | return offset + this.length; 65 | } 66 | 67 | function Depth (depth, visual_types) { 68 | this.depth = depth || 0; 69 | this.visual_types = visual_types || []; 70 | } 71 | 72 | module.exports.Depth = Depth; 73 | 74 | Depth.prototype.__defineGetter__('length', function () { 75 | return 8 + this.visual_types.reduce(function (p, c) { return p + c.length }, 0); 76 | }); 77 | 78 | Depth.prototype.writeBuffer = function (buffer, offset) { 79 | buffer.writeUInt8(this.depth, offset); 80 | // 1 unused 81 | buffer.writeUInt16(this.visual_types.length, offset + 2); 82 | // 4 unused 83 | offset += 8; 84 | offset = this.visual_types.writeBuffer(buffer, offset); 85 | return offset; 86 | } 87 | 88 | function Screen (window, colormap, white, black, current_input_masks, width_px, height_px, width_mm, height_mm, maps_min, maps_max, root_visual, backing_stores, save_unders, root_depth, depths) { 89 | this.window = window || 0; 90 | this.colormap = colormap || 0; 91 | this.white = white || 0; 92 | this.black = black || 0; 93 | this.current_input_masks = current_input_masks || 0; 94 | this.height_px = height_px || 0; 95 | this.width_px = width_px || 0; 96 | this.height_mm = height_mm || 0; 97 | this.width_mm = width_mm || 0; 98 | this.maps_min = maps_min || 0; 99 | this.maps_max = maps_max || 0; 100 | this.root_visual = root_visual || 0; 101 | this.backing_stores = backing_stores || 0; 102 | this.save_unders = save_unders || 0; 103 | this.root_depth = root_depth || 0; 104 | this.depths = depths || []; 105 | } 106 | 107 | module.exports.Screen = Screen; 108 | 109 | Screen.prototype.__defineGetter__('length', function () { 110 | return 40 + this.depths.reduce(function (p, c) { return p + c.length }, 0); 111 | }); 112 | 113 | Screen.prototype.hasDepth = function (_depth) { 114 | return this.depths.map(function (depth) { return depth.depth == _depth }).length !== -1; 115 | } 116 | 117 | Screen.prototype.writeBuffer = function (buffer, offset) { 118 | buffer.writeUInt32(this.window, offset); 119 | buffer.writeUInt32(this.colormap, offset + 4); 120 | buffer.writeUInt32(this.white, offset + 8); 121 | buffer.writeUInt32(this.black, offset + 12); 122 | buffer.writeUInt32(this.current_input_masks, offset + 16); 123 | buffer.writeUInt16(this.height_px, offset + 20); 124 | buffer.writeUInt16(this.width_px, offset + 22); 125 | buffer.writeUInt16(this.height_mm, offset + 24); 126 | buffer.writeUInt16(this.width_mm, offset + 26); 127 | buffer.writeUInt16(this.maps_min, offset + 28); 128 | buffer.writeUInt16(this.maps_max, offset + 30); 129 | buffer.writeUInt32(this.root_visual, offset + 32); 130 | buffer.writeUInt8(this.backing_stores, offset + 36); 131 | buffer.writeUInt8(this.save_unders, offset + 37); 132 | buffer.writeUInt8(this.root_depth, offset + 38); 133 | buffer.writeUInt8(this.depths.length, offset + 39); 134 | return this.depths.writeBuffer(buffer, offset + 40); 135 | } 136 | 137 | function Request (data, sequence) { 138 | this.opcode = data.readUInt8(0); 139 | this.data_byte = data.readUInt8(1); 140 | this.length_quad = data.readUInt16(2); 141 | this.length = this.length_quad * 4; 142 | this.data = data.slice(4); 143 | this.data.endian = data.endian; 144 | this.sequence = sequence; 145 | } 146 | 147 | Request.sequence = 1; 148 | 149 | module.exports.Request = Request; 150 | 151 | function Reply (request) { 152 | this.opcode = request.opcode; 153 | this.sequence = request.sequence; 154 | this.data_byte = 0; 155 | this.data = new Buffer(24); 156 | this.data.endian = request.data.endian; 157 | this.data.fill(0); 158 | this.data_extra = []; 159 | } 160 | 161 | module.exports.Reply = Reply; 162 | 163 | Reply.prototype.__defineGetter__('length', function () { 164 | return 32 + this.data_extra.byteLength(); 165 | }); 166 | 167 | Reply.prototype.writeBuffer = function (buffer, offset) { 168 | buffer.writeUInt8(1, offset); 169 | buffer.writeUInt8(this.data_byte, offset + 1); 170 | buffer.writeUInt16(this.sequence, offset + 2); 171 | buffer.writeUInt32(this.data_extra.byteLength() / 4, offset + 4); 172 | this.data.copy(buffer, offset + 8); 173 | return this.data_extra.writeBuffer(buffer, offset + 8 + 24); 174 | } 175 | 176 | function _Error (req, code, value) { 177 | this.code = code || 1; 178 | this.opcode = req.opcode; 179 | this.opcode_minor = 0; 180 | this.sequence = req.sequence; 181 | this.value = value || 0; 182 | this.length = 32; 183 | } 184 | 185 | module.exports.Error = _Error; 186 | 187 | _Error.prototype.writeBuffer = function (buffer, offset) { 188 | buffer.writeUInt8(0, offset); 189 | buffer.writeUInt8(this.code, offset + 1); 190 | buffer.writeUInt16(this.sequence, offset + 2); 191 | buffer.writeUInt32(this.value, offset + 4); 192 | buffer.writeUInt16(this.opcode, offset + 8); 193 | buffer.writeUInt8(this.opcode_minor, offset + 10); 194 | buffer.fill(0, offset + 11, offset + 32); 195 | return offset + 32; 196 | } 197 | 198 | var _gc_vfields = [ 199 | 'function' , 'plane_mask' , 'foreground' , 'background' 200 | , 'line_width' , 'line_style' , 'cap_style' , 'join_style' , 'fill_style' , 'fill_rule' 201 | , 'tile' , 'stipple', 'tile_stipple_x_origin', 'tile_stipple_y_origin' 202 | , 'font', 'subwindow_mode', 'graphics_exposures', 'clip_x_origin', 'clip_y_origin', 'clip_mask' 203 | , 'dash_offset', 'gc_dashes', 'arc_mode' 204 | ] 205 | 206 | function GraphicsContext (id, drawable, vmask, vdata) { 207 | this.id = id; 208 | this.drawable = drawable; 209 | this.context = drawable.canvas.getContext('2d'); 210 | var offset = 0; 211 | for (var i = 0; i < _gc_vfields.length; i++) 212 | if (vmask & Math.pow(2, i)) 213 | this[_gc_vfields[i]] = vdata.readUInt32((offset ++) * 4); 214 | 215 | console.log('Created GC', this); 216 | } 217 | 218 | module.exports.GraphicsContext = GraphicsContext; 219 | 220 | GraphicsContext.prototype.putImage = function (data, width, height, x, y) { 221 | var rgba = this.context.createImageData(width, height); 222 | switch (this.drawable.depth) { 223 | case 1: 224 | for (var i = 0; i < data.length; i++) { 225 | for (var j = 0; j < 8; j++) { 226 | var offset = ((i * 8) + j) * 4; 227 | var mask = Math.pow(2, j); 228 | if (offset >= rgba.data.length) 229 | break; 230 | rgba.data[offset] = rgba.data[offset + 1] = rgba.data[offset + 2] = ((data[i] & mask) ? 0xff : 0x00); 231 | rgba.data[offset + 3] = 0xff; 232 | } 233 | } 234 | break; 235 | } 236 | this.context.putImageData(rgba, x, y); 237 | } 238 | 239 | function Pixmap (id, depth, drawable, width, height) { 240 | this.id = id; 241 | this.depth = depth; 242 | this.drawable = drawable; 243 | this.width = width; 244 | this.height = height; 245 | // FIXME: create correct buffer size for image! 246 | this.canvas = new Canvas(width, height); 247 | console.log('Created Pixmap', this); 248 | } 249 | 250 | module.exports.Pixmap = Pixmap; 251 | 252 | var _win_vfields = [ 253 | 'background_pixmap', 'background_pixel', 'border_pixmap', 'border_pixel' 254 | , 'bit_gravity', 'win_gravity' 255 | , 'backing_store', 'backing_planes', 'backing_pixel' 256 | , 'override_redirect', 'save_under', 'event_mask', 'do_not_propagate_mask' 257 | , 'colormap', 'cursor' 258 | ]; 259 | 260 | function Window (id, depth, parent, x, y, width, height, border_width, _class, visual, vmask, vdata) { 261 | this.id = id; 262 | this.depth = depth; 263 | this.parent = parent; 264 | this.x = x; 265 | this.y = y; 266 | this.width = width; 267 | this.height = height; 268 | this.border_width = border_width; 269 | this.class = _class; 270 | this.visual = visual; 271 | var offset = 0; 272 | for (var i = 0; i < _gc_vfields.length; i++) 273 | if (vmask & Math.pow(2, i)) 274 | this[_win_vfields[i]] = vdata.readUInt32((offset ++) * 4); 275 | 276 | this.properties = {} 277 | this.canvas = new Canvas(width, height); 278 | } 279 | 280 | module.exports.Window = Window; 281 | 282 | Window.prototype.changeProperty = function (property, format, data, mode) { 283 | mode = mode || 0; 284 | var old; 285 | switch (mode) { 286 | case 0: // Replace 287 | this.properties[property] = data; 288 | this.properties[property].format = format; 289 | break; 290 | case 1: // Prepend 291 | if (this.properties[property].format != format) 292 | throw new Error('Invalid format for this property') 293 | this.properties[property] = new Buffer((old = this.properties[property]).length + data.length); 294 | this.properties[property].endian = data.endian; 295 | data.copy(this.properties[property]); 296 | old.copy (this.properties[property], data.length); 297 | break; 298 | case 2: // Append 299 | if (this.properties[property].format != format) 300 | throw new Error('Invalid format for this property') 301 | this.properties[property] = new Buffer((old = this.properties[property]).length + data.length); 302 | this.properties[property].endian = data.endian; 303 | old.copy (this.properties[property]); 304 | data.copy(this.properties[property], old.length); 305 | break; 306 | } 307 | } 308 | --------------------------------------------------------------------------------